From 53677e3c64f7832c31811e69a9475d964469424e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 13:35:27 +0100 Subject: [PATCH 001/276] Moved views. --- resources/{twig => views}/accounts/create.twig | 0 resources/{twig => views}/accounts/delete.twig | 0 resources/{twig => views}/accounts/edit.twig | 0 resources/{twig => views}/accounts/index.twig | 0 resources/{twig => views}/accounts/show.twig | 0 resources/{twig => views}/attachments/delete.twig | 0 resources/{twig => views}/attachments/edit.twig | 0 resources/{twig => views}/auth/login.twig | 2 +- resources/{twig => views}/auth/password.twig | 0 resources/{twig => views}/auth/register.twig | 0 resources/{twig => views}/auth/reset.twig | 0 resources/{twig => views}/bills/create.twig | 0 resources/{twig => views}/bills/delete.twig | 0 resources/{twig => views}/bills/edit.twig | 0 resources/{twig => views}/bills/index.twig | 0 resources/{twig => views}/bills/show.twig | 0 resources/{twig => views}/budgets/create.twig | 0 resources/{twig => views}/budgets/delete.twig | 0 resources/{twig => views}/budgets/edit.twig | 0 resources/{twig => views}/budgets/income.twig | 0 resources/{twig => views}/budgets/index.twig | 0 resources/{twig => views}/budgets/noBudget.twig | 0 resources/{twig => views}/budgets/show.twig | 0 resources/{twig => views}/categories/create.twig | 0 resources/{twig => views}/categories/delete.twig | 0 resources/{twig => views}/categories/edit.twig | 0 resources/{twig => views}/categories/index.twig | 0 resources/{twig => views}/categories/noCategory.twig | 0 resources/{twig => views}/categories/show.twig | 0 resources/{twig => views}/categories/show_with_date.twig | 0 resources/{twig => views}/csv/column-roles.twig | 0 resources/{twig => views}/csv/download-config.twig | 0 resources/{twig => views}/csv/index.twig | 0 resources/{twig => views}/csv/map.twig | 0 resources/{twig => views}/csv/process.twig | 0 resources/{twig => views}/currency/create.twig | 0 resources/{twig => views}/currency/delete.twig | 0 resources/{twig => views}/currency/edit.twig | 0 resources/{twig => views}/currency/index.twig | 0 resources/{twig => views}/emails/password.twig | 0 resources/{twig => views}/emails/registered-html.twig | 0 resources/{twig => views}/emails/registered.twig | 0 resources/{twig => views}/error.twig | 0 resources/{twig => views}/errors/404.twig | 0 resources/{twig => views}/errors/503.twig | 0 resources/{twig => views}/form/amount.twig | 0 resources/{twig => views}/form/balance.twig | 0 resources/{twig => views}/form/checkbox.twig | 0 resources/{twig => views}/form/date.twig | 0 resources/{twig => views}/form/feedback.twig | 0 resources/{twig => views}/form/file.twig | 0 resources/{twig => views}/form/help.twig | 0 resources/{twig => views}/form/integer.twig | 0 resources/{twig => views}/form/location.twig | 0 resources/{twig => views}/form/multiCheckbox.twig | 0 resources/{twig => views}/form/multiRadio.twig | 0 resources/{twig => views}/form/options.twig | 0 resources/{twig => views}/form/select.twig | 0 resources/{twig => views}/form/static.twig | 0 resources/{twig => views}/form/tags.twig | 0 resources/{twig => views}/form/text.twig | 0 resources/{twig => views}/form/textarea.twig | 0 resources/{twig => views}/index.twig | 0 resources/{twig => views}/json/tour.twig | 0 resources/{twig => views}/layout/default.twig | 0 resources/{twig => views}/layout/empty.twig | 0 resources/{twig => views}/layout/guest.twig | 0 resources/{twig => views}/list/accounts.twig | 0 resources/{twig => views}/list/bills.twig | 0 resources/{twig => views}/list/categories.twig | 0 resources/{twig => views}/list/journals-tiny.twig | 0 resources/{twig => views}/list/journals.twig | 0 resources/{twig => views}/list/piggy-bank-events.twig | 0 resources/{twig => views}/list/piggy-banks.twig | 0 resources/{twig => views}/new-user/index.twig | 0 resources/{twig => views}/partials/boxes.twig | 0 resources/{twig => views}/partials/control-bar.twig | 0 resources/{twig => views}/partials/flashes.twig | 0 resources/{twig => views}/partials/menu-sidebar.twig | 0 resources/{twig => views}/partials/page-header.twig | 0 resources/{twig => views}/piggy-banks/add.twig | 0 resources/{twig => views}/piggy-banks/create.twig | 0 resources/{twig => views}/piggy-banks/delete.twig | 0 resources/{twig => views}/piggy-banks/edit.twig | 0 resources/{twig => views}/piggy-banks/index.twig | 0 resources/{twig => views}/piggy-banks/remove.twig | 0 resources/{twig => views}/piggy-banks/show.twig | 0 resources/{twig => views}/preferences/index.twig | 0 resources/{twig => views}/profile/change-password.twig | 0 resources/{twig => views}/profile/delete-account.twig | 0 resources/{twig => views}/profile/index.twig | 0 resources/{twig => views}/reminders/index.twig | 0 resources/{twig => views}/reminders/show.twig | 0 resources/{twig => views}/reports/default/month.twig | 0 resources/{twig => views}/reports/default/multi-year.twig | 0 resources/{twig => views}/reports/default/year.twig | 0 resources/{twig => views}/reports/index.twig | 0 resources/{twig => views}/reports/partials/accounts.twig | 0 resources/{twig => views}/reports/partials/balance.twig | 0 resources/{twig => views}/reports/partials/bills.twig | 0 resources/{twig => views}/reports/partials/budgets.twig | 0 resources/{twig => views}/reports/partials/categories.twig | 0 resources/{twig => views}/reports/partials/expenses.twig | 0 .../{twig => views}/reports/partials/income-vs-expenses.twig | 0 resources/{twig => views}/reports/partials/income.twig | 0 resources/{twig => views}/search/index.twig | 0 resources/{twig => views}/tags/create.twig | 0 resources/{twig => views}/tags/delete.twig | 0 resources/{twig => views}/tags/edit.twig | 0 resources/{twig => views}/tags/index.twig | 0 resources/{twig => views}/tags/show.twig | 0 resources/{twig => views}/transactions/create.twig | 0 resources/{twig => views}/transactions/delete.twig | 0 resources/{twig => views}/transactions/edit.twig | 0 resources/{twig => views}/transactions/index.twig | 0 resources/{twig => views}/transactions/show.twig | 0 116 files changed, 1 insertion(+), 1 deletion(-) rename resources/{twig => views}/accounts/create.twig (100%) rename resources/{twig => views}/accounts/delete.twig (100%) rename resources/{twig => views}/accounts/edit.twig (100%) rename resources/{twig => views}/accounts/index.twig (100%) rename resources/{twig => views}/accounts/show.twig (100%) rename resources/{twig => views}/attachments/delete.twig (100%) rename resources/{twig => views}/attachments/edit.twig (100%) rename resources/{twig => views}/auth/login.twig (96%) rename resources/{twig => views}/auth/password.twig (100%) rename resources/{twig => views}/auth/register.twig (100%) rename resources/{twig => views}/auth/reset.twig (100%) rename resources/{twig => views}/bills/create.twig (100%) rename resources/{twig => views}/bills/delete.twig (100%) rename resources/{twig => views}/bills/edit.twig (100%) rename resources/{twig => views}/bills/index.twig (100%) rename resources/{twig => views}/bills/show.twig (100%) rename resources/{twig => views}/budgets/create.twig (100%) rename resources/{twig => views}/budgets/delete.twig (100%) rename resources/{twig => views}/budgets/edit.twig (100%) rename resources/{twig => views}/budgets/income.twig (100%) rename resources/{twig => views}/budgets/index.twig (100%) rename resources/{twig => views}/budgets/noBudget.twig (100%) rename resources/{twig => views}/budgets/show.twig (100%) rename resources/{twig => views}/categories/create.twig (100%) rename resources/{twig => views}/categories/delete.twig (100%) rename resources/{twig => views}/categories/edit.twig (100%) rename resources/{twig => views}/categories/index.twig (100%) rename resources/{twig => views}/categories/noCategory.twig (100%) rename resources/{twig => views}/categories/show.twig (100%) rename resources/{twig => views}/categories/show_with_date.twig (100%) rename resources/{twig => views}/csv/column-roles.twig (100%) rename resources/{twig => views}/csv/download-config.twig (100%) rename resources/{twig => views}/csv/index.twig (100%) rename resources/{twig => views}/csv/map.twig (100%) rename resources/{twig => views}/csv/process.twig (100%) rename resources/{twig => views}/currency/create.twig (100%) rename resources/{twig => views}/currency/delete.twig (100%) rename resources/{twig => views}/currency/edit.twig (100%) rename resources/{twig => views}/currency/index.twig (100%) rename resources/{twig => views}/emails/password.twig (100%) rename resources/{twig => views}/emails/registered-html.twig (100%) rename resources/{twig => views}/emails/registered.twig (100%) rename resources/{twig => views}/error.twig (100%) rename resources/{twig => views}/errors/404.twig (100%) rename resources/{twig => views}/errors/503.twig (100%) rename resources/{twig => views}/form/amount.twig (100%) rename resources/{twig => views}/form/balance.twig (100%) rename resources/{twig => views}/form/checkbox.twig (100%) rename resources/{twig => views}/form/date.twig (100%) rename resources/{twig => views}/form/feedback.twig (100%) rename resources/{twig => views}/form/file.twig (100%) rename resources/{twig => views}/form/help.twig (100%) rename resources/{twig => views}/form/integer.twig (100%) rename resources/{twig => views}/form/location.twig (100%) rename resources/{twig => views}/form/multiCheckbox.twig (100%) rename resources/{twig => views}/form/multiRadio.twig (100%) rename resources/{twig => views}/form/options.twig (100%) rename resources/{twig => views}/form/select.twig (100%) rename resources/{twig => views}/form/static.twig (100%) rename resources/{twig => views}/form/tags.twig (100%) rename resources/{twig => views}/form/text.twig (100%) rename resources/{twig => views}/form/textarea.twig (100%) rename resources/{twig => views}/index.twig (100%) rename resources/{twig => views}/json/tour.twig (100%) rename resources/{twig => views}/layout/default.twig (100%) rename resources/{twig => views}/layout/empty.twig (100%) rename resources/{twig => views}/layout/guest.twig (100%) rename resources/{twig => views}/list/accounts.twig (100%) rename resources/{twig => views}/list/bills.twig (100%) rename resources/{twig => views}/list/categories.twig (100%) rename resources/{twig => views}/list/journals-tiny.twig (100%) rename resources/{twig => views}/list/journals.twig (100%) rename resources/{twig => views}/list/piggy-bank-events.twig (100%) rename resources/{twig => views}/list/piggy-banks.twig (100%) rename resources/{twig => views}/new-user/index.twig (100%) rename resources/{twig => views}/partials/boxes.twig (100%) rename resources/{twig => views}/partials/control-bar.twig (100%) rename resources/{twig => views}/partials/flashes.twig (100%) rename resources/{twig => views}/partials/menu-sidebar.twig (100%) rename resources/{twig => views}/partials/page-header.twig (100%) rename resources/{twig => views}/piggy-banks/add.twig (100%) rename resources/{twig => views}/piggy-banks/create.twig (100%) rename resources/{twig => views}/piggy-banks/delete.twig (100%) rename resources/{twig => views}/piggy-banks/edit.twig (100%) rename resources/{twig => views}/piggy-banks/index.twig (100%) rename resources/{twig => views}/piggy-banks/remove.twig (100%) rename resources/{twig => views}/piggy-banks/show.twig (100%) rename resources/{twig => views}/preferences/index.twig (100%) rename resources/{twig => views}/profile/change-password.twig (100%) rename resources/{twig => views}/profile/delete-account.twig (100%) rename resources/{twig => views}/profile/index.twig (100%) rename resources/{twig => views}/reminders/index.twig (100%) rename resources/{twig => views}/reminders/show.twig (100%) rename resources/{twig => views}/reports/default/month.twig (100%) rename resources/{twig => views}/reports/default/multi-year.twig (100%) rename resources/{twig => views}/reports/default/year.twig (100%) rename resources/{twig => views}/reports/index.twig (100%) rename resources/{twig => views}/reports/partials/accounts.twig (100%) rename resources/{twig => views}/reports/partials/balance.twig (100%) rename resources/{twig => views}/reports/partials/bills.twig (100%) rename resources/{twig => views}/reports/partials/budgets.twig (100%) rename resources/{twig => views}/reports/partials/categories.twig (100%) rename resources/{twig => views}/reports/partials/expenses.twig (100%) rename resources/{twig => views}/reports/partials/income-vs-expenses.twig (100%) rename resources/{twig => views}/reports/partials/income.twig (100%) rename resources/{twig => views}/search/index.twig (100%) rename resources/{twig => views}/tags/create.twig (100%) rename resources/{twig => views}/tags/delete.twig (100%) rename resources/{twig => views}/tags/edit.twig (100%) rename resources/{twig => views}/tags/index.twig (100%) rename resources/{twig => views}/tags/show.twig (100%) rename resources/{twig => views}/transactions/create.twig (100%) rename resources/{twig => views}/transactions/delete.twig (100%) rename resources/{twig => views}/transactions/edit.twig (100%) rename resources/{twig => views}/transactions/index.twig (100%) rename resources/{twig => views}/transactions/show.twig (100%) diff --git a/resources/twig/accounts/create.twig b/resources/views/accounts/create.twig similarity index 100% rename from resources/twig/accounts/create.twig rename to resources/views/accounts/create.twig diff --git a/resources/twig/accounts/delete.twig b/resources/views/accounts/delete.twig similarity index 100% rename from resources/twig/accounts/delete.twig rename to resources/views/accounts/delete.twig diff --git a/resources/twig/accounts/edit.twig b/resources/views/accounts/edit.twig similarity index 100% rename from resources/twig/accounts/edit.twig rename to resources/views/accounts/edit.twig diff --git a/resources/twig/accounts/index.twig b/resources/views/accounts/index.twig similarity index 100% rename from resources/twig/accounts/index.twig rename to resources/views/accounts/index.twig diff --git a/resources/twig/accounts/show.twig b/resources/views/accounts/show.twig similarity index 100% rename from resources/twig/accounts/show.twig rename to resources/views/accounts/show.twig diff --git a/resources/twig/attachments/delete.twig b/resources/views/attachments/delete.twig similarity index 100% rename from resources/twig/attachments/delete.twig rename to resources/views/attachments/delete.twig diff --git a/resources/twig/attachments/edit.twig b/resources/views/attachments/edit.twig similarity index 100% rename from resources/twig/attachments/edit.twig rename to resources/views/attachments/edit.twig diff --git a/resources/twig/auth/login.twig b/resources/views/auth/login.twig similarity index 96% rename from resources/twig/auth/login.twig rename to resources/views/auth/login.twig index 88e8d4554e..4b143cf6af 100644 --- a/resources/twig/auth/login.twig +++ b/resources/views/auth/login.twig @@ -17,7 +17,7 @@
-
+
diff --git a/resources/twig/auth/password.twig b/resources/views/auth/password.twig similarity index 100% rename from resources/twig/auth/password.twig rename to resources/views/auth/password.twig diff --git a/resources/twig/auth/register.twig b/resources/views/auth/register.twig similarity index 100% rename from resources/twig/auth/register.twig rename to resources/views/auth/register.twig diff --git a/resources/twig/auth/reset.twig b/resources/views/auth/reset.twig similarity index 100% rename from resources/twig/auth/reset.twig rename to resources/views/auth/reset.twig diff --git a/resources/twig/bills/create.twig b/resources/views/bills/create.twig similarity index 100% rename from resources/twig/bills/create.twig rename to resources/views/bills/create.twig diff --git a/resources/twig/bills/delete.twig b/resources/views/bills/delete.twig similarity index 100% rename from resources/twig/bills/delete.twig rename to resources/views/bills/delete.twig diff --git a/resources/twig/bills/edit.twig b/resources/views/bills/edit.twig similarity index 100% rename from resources/twig/bills/edit.twig rename to resources/views/bills/edit.twig diff --git a/resources/twig/bills/index.twig b/resources/views/bills/index.twig similarity index 100% rename from resources/twig/bills/index.twig rename to resources/views/bills/index.twig diff --git a/resources/twig/bills/show.twig b/resources/views/bills/show.twig similarity index 100% rename from resources/twig/bills/show.twig rename to resources/views/bills/show.twig diff --git a/resources/twig/budgets/create.twig b/resources/views/budgets/create.twig similarity index 100% rename from resources/twig/budgets/create.twig rename to resources/views/budgets/create.twig diff --git a/resources/twig/budgets/delete.twig b/resources/views/budgets/delete.twig similarity index 100% rename from resources/twig/budgets/delete.twig rename to resources/views/budgets/delete.twig diff --git a/resources/twig/budgets/edit.twig b/resources/views/budgets/edit.twig similarity index 100% rename from resources/twig/budgets/edit.twig rename to resources/views/budgets/edit.twig diff --git a/resources/twig/budgets/income.twig b/resources/views/budgets/income.twig similarity index 100% rename from resources/twig/budgets/income.twig rename to resources/views/budgets/income.twig diff --git a/resources/twig/budgets/index.twig b/resources/views/budgets/index.twig similarity index 100% rename from resources/twig/budgets/index.twig rename to resources/views/budgets/index.twig diff --git a/resources/twig/budgets/noBudget.twig b/resources/views/budgets/noBudget.twig similarity index 100% rename from resources/twig/budgets/noBudget.twig rename to resources/views/budgets/noBudget.twig diff --git a/resources/twig/budgets/show.twig b/resources/views/budgets/show.twig similarity index 100% rename from resources/twig/budgets/show.twig rename to resources/views/budgets/show.twig diff --git a/resources/twig/categories/create.twig b/resources/views/categories/create.twig similarity index 100% rename from resources/twig/categories/create.twig rename to resources/views/categories/create.twig diff --git a/resources/twig/categories/delete.twig b/resources/views/categories/delete.twig similarity index 100% rename from resources/twig/categories/delete.twig rename to resources/views/categories/delete.twig diff --git a/resources/twig/categories/edit.twig b/resources/views/categories/edit.twig similarity index 100% rename from resources/twig/categories/edit.twig rename to resources/views/categories/edit.twig diff --git a/resources/twig/categories/index.twig b/resources/views/categories/index.twig similarity index 100% rename from resources/twig/categories/index.twig rename to resources/views/categories/index.twig diff --git a/resources/twig/categories/noCategory.twig b/resources/views/categories/noCategory.twig similarity index 100% rename from resources/twig/categories/noCategory.twig rename to resources/views/categories/noCategory.twig diff --git a/resources/twig/categories/show.twig b/resources/views/categories/show.twig similarity index 100% rename from resources/twig/categories/show.twig rename to resources/views/categories/show.twig diff --git a/resources/twig/categories/show_with_date.twig b/resources/views/categories/show_with_date.twig similarity index 100% rename from resources/twig/categories/show_with_date.twig rename to resources/views/categories/show_with_date.twig diff --git a/resources/twig/csv/column-roles.twig b/resources/views/csv/column-roles.twig similarity index 100% rename from resources/twig/csv/column-roles.twig rename to resources/views/csv/column-roles.twig diff --git a/resources/twig/csv/download-config.twig b/resources/views/csv/download-config.twig similarity index 100% rename from resources/twig/csv/download-config.twig rename to resources/views/csv/download-config.twig diff --git a/resources/twig/csv/index.twig b/resources/views/csv/index.twig similarity index 100% rename from resources/twig/csv/index.twig rename to resources/views/csv/index.twig diff --git a/resources/twig/csv/map.twig b/resources/views/csv/map.twig similarity index 100% rename from resources/twig/csv/map.twig rename to resources/views/csv/map.twig diff --git a/resources/twig/csv/process.twig b/resources/views/csv/process.twig similarity index 100% rename from resources/twig/csv/process.twig rename to resources/views/csv/process.twig diff --git a/resources/twig/currency/create.twig b/resources/views/currency/create.twig similarity index 100% rename from resources/twig/currency/create.twig rename to resources/views/currency/create.twig diff --git a/resources/twig/currency/delete.twig b/resources/views/currency/delete.twig similarity index 100% rename from resources/twig/currency/delete.twig rename to resources/views/currency/delete.twig diff --git a/resources/twig/currency/edit.twig b/resources/views/currency/edit.twig similarity index 100% rename from resources/twig/currency/edit.twig rename to resources/views/currency/edit.twig diff --git a/resources/twig/currency/index.twig b/resources/views/currency/index.twig similarity index 100% rename from resources/twig/currency/index.twig rename to resources/views/currency/index.twig diff --git a/resources/twig/emails/password.twig b/resources/views/emails/password.twig similarity index 100% rename from resources/twig/emails/password.twig rename to resources/views/emails/password.twig diff --git a/resources/twig/emails/registered-html.twig b/resources/views/emails/registered-html.twig similarity index 100% rename from resources/twig/emails/registered-html.twig rename to resources/views/emails/registered-html.twig diff --git a/resources/twig/emails/registered.twig b/resources/views/emails/registered.twig similarity index 100% rename from resources/twig/emails/registered.twig rename to resources/views/emails/registered.twig diff --git a/resources/twig/error.twig b/resources/views/error.twig similarity index 100% rename from resources/twig/error.twig rename to resources/views/error.twig diff --git a/resources/twig/errors/404.twig b/resources/views/errors/404.twig similarity index 100% rename from resources/twig/errors/404.twig rename to resources/views/errors/404.twig diff --git a/resources/twig/errors/503.twig b/resources/views/errors/503.twig similarity index 100% rename from resources/twig/errors/503.twig rename to resources/views/errors/503.twig diff --git a/resources/twig/form/amount.twig b/resources/views/form/amount.twig similarity index 100% rename from resources/twig/form/amount.twig rename to resources/views/form/amount.twig diff --git a/resources/twig/form/balance.twig b/resources/views/form/balance.twig similarity index 100% rename from resources/twig/form/balance.twig rename to resources/views/form/balance.twig diff --git a/resources/twig/form/checkbox.twig b/resources/views/form/checkbox.twig similarity index 100% rename from resources/twig/form/checkbox.twig rename to resources/views/form/checkbox.twig diff --git a/resources/twig/form/date.twig b/resources/views/form/date.twig similarity index 100% rename from resources/twig/form/date.twig rename to resources/views/form/date.twig diff --git a/resources/twig/form/feedback.twig b/resources/views/form/feedback.twig similarity index 100% rename from resources/twig/form/feedback.twig rename to resources/views/form/feedback.twig diff --git a/resources/twig/form/file.twig b/resources/views/form/file.twig similarity index 100% rename from resources/twig/form/file.twig rename to resources/views/form/file.twig diff --git a/resources/twig/form/help.twig b/resources/views/form/help.twig similarity index 100% rename from resources/twig/form/help.twig rename to resources/views/form/help.twig diff --git a/resources/twig/form/integer.twig b/resources/views/form/integer.twig similarity index 100% rename from resources/twig/form/integer.twig rename to resources/views/form/integer.twig diff --git a/resources/twig/form/location.twig b/resources/views/form/location.twig similarity index 100% rename from resources/twig/form/location.twig rename to resources/views/form/location.twig diff --git a/resources/twig/form/multiCheckbox.twig b/resources/views/form/multiCheckbox.twig similarity index 100% rename from resources/twig/form/multiCheckbox.twig rename to resources/views/form/multiCheckbox.twig diff --git a/resources/twig/form/multiRadio.twig b/resources/views/form/multiRadio.twig similarity index 100% rename from resources/twig/form/multiRadio.twig rename to resources/views/form/multiRadio.twig diff --git a/resources/twig/form/options.twig b/resources/views/form/options.twig similarity index 100% rename from resources/twig/form/options.twig rename to resources/views/form/options.twig diff --git a/resources/twig/form/select.twig b/resources/views/form/select.twig similarity index 100% rename from resources/twig/form/select.twig rename to resources/views/form/select.twig diff --git a/resources/twig/form/static.twig b/resources/views/form/static.twig similarity index 100% rename from resources/twig/form/static.twig rename to resources/views/form/static.twig diff --git a/resources/twig/form/tags.twig b/resources/views/form/tags.twig similarity index 100% rename from resources/twig/form/tags.twig rename to resources/views/form/tags.twig diff --git a/resources/twig/form/text.twig b/resources/views/form/text.twig similarity index 100% rename from resources/twig/form/text.twig rename to resources/views/form/text.twig diff --git a/resources/twig/form/textarea.twig b/resources/views/form/textarea.twig similarity index 100% rename from resources/twig/form/textarea.twig rename to resources/views/form/textarea.twig diff --git a/resources/twig/index.twig b/resources/views/index.twig similarity index 100% rename from resources/twig/index.twig rename to resources/views/index.twig diff --git a/resources/twig/json/tour.twig b/resources/views/json/tour.twig similarity index 100% rename from resources/twig/json/tour.twig rename to resources/views/json/tour.twig diff --git a/resources/twig/layout/default.twig b/resources/views/layout/default.twig similarity index 100% rename from resources/twig/layout/default.twig rename to resources/views/layout/default.twig diff --git a/resources/twig/layout/empty.twig b/resources/views/layout/empty.twig similarity index 100% rename from resources/twig/layout/empty.twig rename to resources/views/layout/empty.twig diff --git a/resources/twig/layout/guest.twig b/resources/views/layout/guest.twig similarity index 100% rename from resources/twig/layout/guest.twig rename to resources/views/layout/guest.twig diff --git a/resources/twig/list/accounts.twig b/resources/views/list/accounts.twig similarity index 100% rename from resources/twig/list/accounts.twig rename to resources/views/list/accounts.twig diff --git a/resources/twig/list/bills.twig b/resources/views/list/bills.twig similarity index 100% rename from resources/twig/list/bills.twig rename to resources/views/list/bills.twig diff --git a/resources/twig/list/categories.twig b/resources/views/list/categories.twig similarity index 100% rename from resources/twig/list/categories.twig rename to resources/views/list/categories.twig diff --git a/resources/twig/list/journals-tiny.twig b/resources/views/list/journals-tiny.twig similarity index 100% rename from resources/twig/list/journals-tiny.twig rename to resources/views/list/journals-tiny.twig diff --git a/resources/twig/list/journals.twig b/resources/views/list/journals.twig similarity index 100% rename from resources/twig/list/journals.twig rename to resources/views/list/journals.twig diff --git a/resources/twig/list/piggy-bank-events.twig b/resources/views/list/piggy-bank-events.twig similarity index 100% rename from resources/twig/list/piggy-bank-events.twig rename to resources/views/list/piggy-bank-events.twig diff --git a/resources/twig/list/piggy-banks.twig b/resources/views/list/piggy-banks.twig similarity index 100% rename from resources/twig/list/piggy-banks.twig rename to resources/views/list/piggy-banks.twig diff --git a/resources/twig/new-user/index.twig b/resources/views/new-user/index.twig similarity index 100% rename from resources/twig/new-user/index.twig rename to resources/views/new-user/index.twig diff --git a/resources/twig/partials/boxes.twig b/resources/views/partials/boxes.twig similarity index 100% rename from resources/twig/partials/boxes.twig rename to resources/views/partials/boxes.twig diff --git a/resources/twig/partials/control-bar.twig b/resources/views/partials/control-bar.twig similarity index 100% rename from resources/twig/partials/control-bar.twig rename to resources/views/partials/control-bar.twig diff --git a/resources/twig/partials/flashes.twig b/resources/views/partials/flashes.twig similarity index 100% rename from resources/twig/partials/flashes.twig rename to resources/views/partials/flashes.twig diff --git a/resources/twig/partials/menu-sidebar.twig b/resources/views/partials/menu-sidebar.twig similarity index 100% rename from resources/twig/partials/menu-sidebar.twig rename to resources/views/partials/menu-sidebar.twig diff --git a/resources/twig/partials/page-header.twig b/resources/views/partials/page-header.twig similarity index 100% rename from resources/twig/partials/page-header.twig rename to resources/views/partials/page-header.twig diff --git a/resources/twig/piggy-banks/add.twig b/resources/views/piggy-banks/add.twig similarity index 100% rename from resources/twig/piggy-banks/add.twig rename to resources/views/piggy-banks/add.twig diff --git a/resources/twig/piggy-banks/create.twig b/resources/views/piggy-banks/create.twig similarity index 100% rename from resources/twig/piggy-banks/create.twig rename to resources/views/piggy-banks/create.twig diff --git a/resources/twig/piggy-banks/delete.twig b/resources/views/piggy-banks/delete.twig similarity index 100% rename from resources/twig/piggy-banks/delete.twig rename to resources/views/piggy-banks/delete.twig diff --git a/resources/twig/piggy-banks/edit.twig b/resources/views/piggy-banks/edit.twig similarity index 100% rename from resources/twig/piggy-banks/edit.twig rename to resources/views/piggy-banks/edit.twig diff --git a/resources/twig/piggy-banks/index.twig b/resources/views/piggy-banks/index.twig similarity index 100% rename from resources/twig/piggy-banks/index.twig rename to resources/views/piggy-banks/index.twig diff --git a/resources/twig/piggy-banks/remove.twig b/resources/views/piggy-banks/remove.twig similarity index 100% rename from resources/twig/piggy-banks/remove.twig rename to resources/views/piggy-banks/remove.twig diff --git a/resources/twig/piggy-banks/show.twig b/resources/views/piggy-banks/show.twig similarity index 100% rename from resources/twig/piggy-banks/show.twig rename to resources/views/piggy-banks/show.twig diff --git a/resources/twig/preferences/index.twig b/resources/views/preferences/index.twig similarity index 100% rename from resources/twig/preferences/index.twig rename to resources/views/preferences/index.twig diff --git a/resources/twig/profile/change-password.twig b/resources/views/profile/change-password.twig similarity index 100% rename from resources/twig/profile/change-password.twig rename to resources/views/profile/change-password.twig diff --git a/resources/twig/profile/delete-account.twig b/resources/views/profile/delete-account.twig similarity index 100% rename from resources/twig/profile/delete-account.twig rename to resources/views/profile/delete-account.twig diff --git a/resources/twig/profile/index.twig b/resources/views/profile/index.twig similarity index 100% rename from resources/twig/profile/index.twig rename to resources/views/profile/index.twig diff --git a/resources/twig/reminders/index.twig b/resources/views/reminders/index.twig similarity index 100% rename from resources/twig/reminders/index.twig rename to resources/views/reminders/index.twig diff --git a/resources/twig/reminders/show.twig b/resources/views/reminders/show.twig similarity index 100% rename from resources/twig/reminders/show.twig rename to resources/views/reminders/show.twig diff --git a/resources/twig/reports/default/month.twig b/resources/views/reports/default/month.twig similarity index 100% rename from resources/twig/reports/default/month.twig rename to resources/views/reports/default/month.twig diff --git a/resources/twig/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig similarity index 100% rename from resources/twig/reports/default/multi-year.twig rename to resources/views/reports/default/multi-year.twig diff --git a/resources/twig/reports/default/year.twig b/resources/views/reports/default/year.twig similarity index 100% rename from resources/twig/reports/default/year.twig rename to resources/views/reports/default/year.twig diff --git a/resources/twig/reports/index.twig b/resources/views/reports/index.twig similarity index 100% rename from resources/twig/reports/index.twig rename to resources/views/reports/index.twig diff --git a/resources/twig/reports/partials/accounts.twig b/resources/views/reports/partials/accounts.twig similarity index 100% rename from resources/twig/reports/partials/accounts.twig rename to resources/views/reports/partials/accounts.twig diff --git a/resources/twig/reports/partials/balance.twig b/resources/views/reports/partials/balance.twig similarity index 100% rename from resources/twig/reports/partials/balance.twig rename to resources/views/reports/partials/balance.twig diff --git a/resources/twig/reports/partials/bills.twig b/resources/views/reports/partials/bills.twig similarity index 100% rename from resources/twig/reports/partials/bills.twig rename to resources/views/reports/partials/bills.twig diff --git a/resources/twig/reports/partials/budgets.twig b/resources/views/reports/partials/budgets.twig similarity index 100% rename from resources/twig/reports/partials/budgets.twig rename to resources/views/reports/partials/budgets.twig diff --git a/resources/twig/reports/partials/categories.twig b/resources/views/reports/partials/categories.twig similarity index 100% rename from resources/twig/reports/partials/categories.twig rename to resources/views/reports/partials/categories.twig diff --git a/resources/twig/reports/partials/expenses.twig b/resources/views/reports/partials/expenses.twig similarity index 100% rename from resources/twig/reports/partials/expenses.twig rename to resources/views/reports/partials/expenses.twig diff --git a/resources/twig/reports/partials/income-vs-expenses.twig b/resources/views/reports/partials/income-vs-expenses.twig similarity index 100% rename from resources/twig/reports/partials/income-vs-expenses.twig rename to resources/views/reports/partials/income-vs-expenses.twig diff --git a/resources/twig/reports/partials/income.twig b/resources/views/reports/partials/income.twig similarity index 100% rename from resources/twig/reports/partials/income.twig rename to resources/views/reports/partials/income.twig diff --git a/resources/twig/search/index.twig b/resources/views/search/index.twig similarity index 100% rename from resources/twig/search/index.twig rename to resources/views/search/index.twig diff --git a/resources/twig/tags/create.twig b/resources/views/tags/create.twig similarity index 100% rename from resources/twig/tags/create.twig rename to resources/views/tags/create.twig diff --git a/resources/twig/tags/delete.twig b/resources/views/tags/delete.twig similarity index 100% rename from resources/twig/tags/delete.twig rename to resources/views/tags/delete.twig diff --git a/resources/twig/tags/edit.twig b/resources/views/tags/edit.twig similarity index 100% rename from resources/twig/tags/edit.twig rename to resources/views/tags/edit.twig diff --git a/resources/twig/tags/index.twig b/resources/views/tags/index.twig similarity index 100% rename from resources/twig/tags/index.twig rename to resources/views/tags/index.twig diff --git a/resources/twig/tags/show.twig b/resources/views/tags/show.twig similarity index 100% rename from resources/twig/tags/show.twig rename to resources/views/tags/show.twig diff --git a/resources/twig/transactions/create.twig b/resources/views/transactions/create.twig similarity index 100% rename from resources/twig/transactions/create.twig rename to resources/views/transactions/create.twig diff --git a/resources/twig/transactions/delete.twig b/resources/views/transactions/delete.twig similarity index 100% rename from resources/twig/transactions/delete.twig rename to resources/views/transactions/delete.twig diff --git a/resources/twig/transactions/edit.twig b/resources/views/transactions/edit.twig similarity index 100% rename from resources/twig/transactions/edit.twig rename to resources/views/transactions/edit.twig diff --git a/resources/twig/transactions/index.twig b/resources/views/transactions/index.twig similarity index 100% rename from resources/twig/transactions/index.twig rename to resources/views/transactions/index.twig diff --git a/resources/twig/transactions/show.twig b/resources/views/transactions/show.twig similarity index 100% rename from resources/twig/transactions/show.twig rename to resources/views/transactions/show.twig From 66703b30b3ab975839a40ab54b65777086683f8b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:27:05 +0100 Subject: [PATCH 002/276] Updated git files after laravel 5.2 upgrade --- .gitattributes | 2 + .gitignore | 39 +++---------------- app/{Handlers/Commands => Listeners}/.gitkeep | 0 app/{Handlers/Events => Policies}/.gitkeep | 0 bootstrap/cache/.gitignore | 0 build/logs/.gitignore | 2 - database/.gitignore | 2 +- database/migrations/.gitkeep | 0 database/seeds/.gitkeep | 0 resources/views/vendor/.gitkeep | 0 storage/.gitignore | 1 - storage/app/.gitignore | 2 +- storage/database/.gitignore | 1 - storage/debugbar/.gitignore | 2 - storage/framework/.gitignore | 1 + storage/framework/cache/.gitignore | 2 +- storage/framework/sessions/.gitignore | 0 storage/framework/views/.gitignore | 0 storage/logs/.gitignore | 0 storage/upload/.gitignore | 2 - tests/_output/.gitignore | 2 - 21 files changed, 12 insertions(+), 46 deletions(-) mode change 100644 => 100755 .gitattributes mode change 100644 => 100755 .gitignore rename app/{Handlers/Commands => Listeners}/.gitkeep (100%) mode change 100644 => 100755 rename app/{Handlers/Events => Policies}/.gitkeep (100%) mode change 100644 => 100755 mode change 100644 => 100755 bootstrap/cache/.gitignore delete mode 100644 build/logs/.gitignore mode change 100644 => 100755 database/.gitignore mode change 100644 => 100755 database/migrations/.gitkeep mode change 100644 => 100755 database/seeds/.gitkeep create mode 100755 resources/views/vendor/.gitkeep delete mode 100644 storage/.gitignore mode change 100644 => 100755 storage/app/.gitignore delete mode 100644 storage/database/.gitignore delete mode 100644 storage/debugbar/.gitignore mode change 100644 => 100755 storage/framework/.gitignore mode change 100644 => 100755 storage/framework/cache/.gitignore mode change 100644 => 100755 storage/framework/sessions/.gitignore mode change 100644 => 100755 storage/framework/views/.gitignore mode change 100644 => 100755 storage/logs/.gitignore delete mode 100644 storage/upload/.gitignore delete mode 100644 tests/_output/.gitignore diff --git a/.gitattributes b/.gitattributes old mode 100644 new mode 100755 index 176a458f94..95883deab5 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ * text=auto +*.css linguist-vendored +*.less linguist-vendored diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 5cb379f496..24797db2e7 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,9 @@ -/bootstrap/compiled.php /vendor -composer.phar -Thumbs.db -.idea/ -tests/_output/* -_ide_helper.php -/build/logs -index.html* -app/storage/firefly-export* -.vagrant -firefly-iii-import-*.json -tests/_output/* -testing.sqlite -_ide_helper_models.php -clean.sqlite -tests/acceptance/AcceptanceTester.php -tests/functional/FunctionalTester.php -tests/unit/UnitTester.php -pi.php -tests/_data/db.sqlite -tests/_data/dump.sql -db.sqlite_snapshot -c3.php -db.sqlite-journal -tests/_output/* +/node_modules +Homestead.yaml +Homestead.json .env -clover.xml -node_modules/ -addNewLines.php +.idea/ +_ide_helper.php +_ide_helper_models.php .phpstorm.meta.php -.env.backup -.env.local - -tests/_output/* -tests/_output/* diff --git a/app/Handlers/Commands/.gitkeep b/app/Listeners/.gitkeep old mode 100644 new mode 100755 similarity index 100% rename from app/Handlers/Commands/.gitkeep rename to app/Listeners/.gitkeep diff --git a/app/Handlers/Events/.gitkeep b/app/Policies/.gitkeep old mode 100644 new mode 100755 similarity index 100% rename from app/Handlers/Events/.gitkeep rename to app/Policies/.gitkeep diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100644 new mode 100755 diff --git a/build/logs/.gitignore b/build/logs/.gitignore deleted file mode 100644 index 67c51fe2b0..0000000000 --- a/build/logs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.xml -*.json diff --git a/database/.gitignore b/database/.gitignore old mode 100644 new mode 100755 index 8b13789179..9b1dffd90f --- a/database/.gitignore +++ b/database/.gitignore @@ -1 +1 @@ - +*.sqlite diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep old mode 100644 new mode 100755 diff --git a/database/seeds/.gitkeep b/database/seeds/.gitkeep old mode 100644 new mode 100755 diff --git a/resources/views/vendor/.gitkeep b/resources/views/vendor/.gitkeep new file mode 100755 index 0000000000..e69de29bb2 diff --git a/storage/.gitignore b/storage/.gitignore deleted file mode 100644 index 4ca7e82b3a..0000000000 --- a/storage/.gitignore +++ /dev/null @@ -1 +0,0 @@ -laravel.log diff --git a/storage/app/.gitignore b/storage/app/.gitignore old mode 100644 new mode 100755 index d6b7ef32c8..c96a04f008 --- a/storage/app/.gitignore +++ b/storage/app/.gitignore @@ -1,2 +1,2 @@ * -!.gitignore +!.gitignore \ No newline at end of file diff --git a/storage/database/.gitignore b/storage/database/.gitignore deleted file mode 100644 index 98e6ef67fa..0000000000 --- a/storage/database/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.db diff --git a/storage/debugbar/.gitignore b/storage/debugbar/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/storage/debugbar/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore old mode 100644 new mode 100755 index 1670e90661..953edb7a99 --- a/storage/framework/.gitignore +++ b/storage/framework/.gitignore @@ -4,3 +4,4 @@ compiled.php services.json events.scanned.php routes.scanned.php +down diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore old mode 100644 new mode 100755 index d6b7ef32c8..c96a04f008 --- a/storage/framework/cache/.gitignore +++ b/storage/framework/cache/.gitignore @@ -1,2 +1,2 @@ * -!.gitignore +!.gitignore \ No newline at end of file diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore old mode 100644 new mode 100755 diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore old mode 100644 new mode 100755 diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore old mode 100644 new mode 100755 diff --git a/storage/upload/.gitignore b/storage/upload/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/storage/upload/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/_output/.gitignore b/tests/_output/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/tests/_output/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore From b7580a5f83a3cde8d4178e1797c1cae3f117c1f8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:27:27 +0100 Subject: [PATCH 003/276] Updated example env files after upgrading to laraval 5.2 --- .env.example | 16 +++++++++++----- .env.testing | 18 ------------------ 2 files changed, 11 insertions(+), 23 deletions(-) mode change 100644 => 100755 .env.example delete mode 100644 .env.testing diff --git a/.env.example b/.env.example old mode 100644 new mode 100755 index 1784ebbb83..4d8b66e390 --- a/.env.example +++ b/.env.example @@ -11,15 +11,21 @@ DB_PASSWORD=secret CACHE_DRIVER=file SESSION_DRIVER=file +QUEUE_DRIVER=sync DEFAULT_CURRENCY=EUR DEFAULT_LANGUAGE=en_US -EMAIL_SMTP= -EMAIL_DRIVER=smtp -EMAIL_USERNAME= -EMAIL_PASSWORD= -EMAIL_PRETEND=false +REDIS_HOST=localhost +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null SHOW_INCOMPLETE_TRANSLATIONS=false diff --git a/.env.testing b/.env.testing deleted file mode 100644 index 5443422bda..0000000000 --- a/.env.testing +++ /dev/null @@ -1,18 +0,0 @@ -APP_ENV=testing -APP_DEBUG=true -APP_KEY=SomeRandomString - -DB_CONNECTION=sqlite -DB_HOST=localhost -DB_DATABASE=homestead -DB_USERNAME=homestead -DB_PASSWORD=secret - -CACHE_DRIVER=array -SESSION_DRIVER=array - -EMAIL_SMTP= -EMAIL_USERNAME= -EMAIL_PASSWORD= -ANALYTICS_ID=ABC -EMAIL_PRETEND=true \ No newline at end of file From 8157f0a958bb2b6ce3ad7b6f82a28a9772bc7dff Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:27:35 +0100 Subject: [PATCH 004/276] Updated htaccess file after upgrading to laraval 5.2 --- public/.htaccess | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) mode change 100644 => 100755 public/.htaccess diff --git a/public/.htaccess b/public/.htaccess old mode 100644 new mode 100755 index 77827ae705..8eb2dd0ddf --- a/public/.htaccess +++ b/public/.htaccess @@ -5,7 +5,8 @@ RewriteEngine On - # Redirect Trailing Slashes... + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)/$ /$1 [L,R=301] # Handle Front Controller... From d9884ddf73a7b95f95782d7ec617e4136687b0df Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:28:04 +0100 Subject: [PATCH 005/276] Removed test code after upgrading to laravel 5.2 --- tests/_bootstrap.php | 2 - tests/_support/AcceptanceTester.php | 26 - tests/_support/Helper/Acceptance.php | 14 - .../_generated/AcceptanceTesterActions.php | 1977 ----------------- tests/acceptance.suite.yml | 12 - tests/acceptance/_bootstrap.php | 2 - tests/functional/_bootstrap.php | 2 - tests/unit/_bootstrap.php | 2 - 8 files changed, 2037 deletions(-) delete mode 100644 tests/_bootstrap.php delete mode 100644 tests/_support/AcceptanceTester.php delete mode 100644 tests/_support/Helper/Acceptance.php delete mode 100644 tests/_support/_generated/AcceptanceTesterActions.php delete mode 100644 tests/acceptance.suite.yml delete mode 100644 tests/acceptance/_bootstrap.php delete mode 100644 tests/functional/_bootstrap.php delete mode 100644 tests/unit/_bootstrap.php diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php deleted file mode 100644 index 243f9c85bc..0000000000 --- a/tests/_bootstrap.php +++ /dev/null @@ -1,2 +0,0 @@ -setHeader('X-Requested-With', 'Codeception'); - * $I->amOnPage('test-headers.php'); - * ?> - * ``` - * - * @param string $name the name of the request header - * @param string $value the value to set it to for subsequent - * requests - * @see \Codeception\Module\PhpBrowser::setHeader() - */ - public function setHeader($name, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('setHeader', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Deletes the header with the passed name. Subsequent requests - * will not have the deleted header in its request. - * - * Example: - * ```php - * setHeader('X-Requested-With', 'Codeception'); - * $I->amOnPage('test-headers.php'); - * // ... - * $I->deleteHeader('X-Requested-With'); - * $I->amOnPage('some-other-page.php'); - * ?> - * ``` - * - * @param string $name the name of the header to delete. - * @see \Codeception\Module\PhpBrowser::deleteHeader() - */ - public function deleteHeader($name) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('deleteHeader', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Authenticates user for HTTP_AUTH - * - * @param $username - * @param $password - * @see \Codeception\Module\PhpBrowser::amHttpAuthenticated() - */ - public function amHttpAuthenticated($username, $password) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Open web page at the given absolute URL and sets its hostname as the base host. - * - * ``` php - * amOnUrl('http://codeception.com'); - * $I->amOnPage('/quickstart'); // moves to http://codeception.com/quickstart - * ?> - * ``` - * @see \Codeception\Module\PhpBrowser::amOnUrl() - */ - public function amOnUrl($url) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Changes the subdomain for the 'url' configuration parameter. - * Does not open a page; use `amOnPage` for that. - * - * ``` php - * amOnSubdomain('user'); - * $I->amOnPage('/'); - * // moves to http://user.mysite.com/ - * ?> - * ``` - * - * @param $subdomain - * - * @return mixed - * @see \Codeception\Module\PhpBrowser::amOnSubdomain() - */ - public function amOnSubdomain($subdomain) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnSubdomain', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Low-level API method. - * If Codeception commands are not enough, use [Guzzle HTTP Client](http://guzzlephp.org/) methods directly - * - * Example: - * - * ``` php - * executeInGuzzle(function (\GuzzleHttp\Client $client) { - * $client->get('/get', ['query' => ['foo' => 'bar']]); - * }); - * ?> - * ``` - * - * It is not recommended to use this command on a regular basis. - * If Codeception lacks important Guzzle Client methods, implement them and submit patches. - * - * @param callable $function - * @see \Codeception\Module\PhpBrowser::executeInGuzzle() - */ - public function executeInGuzzle($function) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('executeInGuzzle', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Opens the page for the given relative URI. - * - * ``` php - * amOnPage('/'); - * // opens /register page - * $I->amOnPage('/register'); - * ?> - * ``` - * - * @param $page - * @see \Codeception\Lib\InnerBrowser::amOnPage() - */ - public function amOnPage($page) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Perform a click on a link or a button, given by a locator. - * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. - * For buttons, the "value" attribute, "name" attribute, and inner text are searched. - * For links, the link text is searched. - * For images, the "alt" attribute and inner text of any parent links are searched. - * - * The second parameter is a context (CSS or XPath locator) to narrow the search. - * - * Note that if the locator matches a button of type `submit`, the form will be submitted. - * - * ``` php - * click('Logout'); - * // button of form - * $I->click('Submit'); - * // CSS button - * $I->click('#form input[type=submit]'); - * // XPath - * $I->click('//form/*[@type=submit]'); - * // link in context - * $I->click('Logout', '#nav'); - * // using strict locator - * $I->click(['link' => 'Login']); - * ?> - * ``` - * - * @param $link - * @param $context - * @see \Codeception\Lib\InnerBrowser::click() - */ - public function click($link, $context = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('click', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. - * - * ``` php - * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::see() - */ - public function canSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. - * - * ``` php - * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * @see \Codeception\Lib\InnerBrowser::see() - */ - public function see($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('see', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page doesn't contain the text specified. - * Give a locator as the second parameter to match a specific region. - * - * ```php - * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSee() - */ - public function cantSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page doesn't contain the text specified. - * Give a locator as the second parameter to match a specific region. - * - * ```php - * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * @see \Codeception\Lib\InnerBrowser::dontSee() - */ - public function dontSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there's a link with the specified text. - * Give a full URL as the second parameter to match links with that exact URL. - * - * ``` php - * seeLink('Logout'); // matches Logout - * $I->seeLink('Logout','/logout'); // matches Logout - * ?> - * ``` - * - * @param $text - * @param null $url - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeLink() - */ - public function canSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there's a link with the specified text. - * Give a full URL as the second parameter to match links with that exact URL. - * - * ``` php - * seeLink('Logout'); // matches Logout - * $I->seeLink('Logout','/logout'); // matches Logout - * ?> - * ``` - * - * @param $text - * @param null $url - * @see \Codeception\Lib\InnerBrowser::seeLink() - */ - public function seeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page doesn't contain a link with the given string. - * If the second parameter is given, only links with a matching "href" attribute will be checked. - * - * ``` php - * dontSeeLink('Logout'); // I suppose user is not logged in - * $I->dontSeeLink('Checkout now', '/store/cart.php'); - * ?> - * ``` - * - * @param $text - * @param null $url - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeLink() - */ - public function cantSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page doesn't contain a link with the given string. - * If the second parameter is given, only links with a matching "href" attribute will be checked. - * - * ``` php - * dontSeeLink('Logout'); // I suppose user is not logged in - * $I->dontSeeLink('Checkout now', '/store/cart.php'); - * ?> - * ``` - * - * @param $text - * @param null $url - * @see \Codeception\Lib\InnerBrowser::dontSeeLink() - */ - public function dontSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current URI contains the given string. - * - * ``` php - * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() - */ - public function canSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current URI contains the given string. - * - * ``` php - * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() - */ - public function seeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URI doesn't contain the given string. - * - * ``` php - * dontSeeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() - */ - public function cantSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URI doesn't contain the given string. - * - * ``` php - * dontSeeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() - */ - public function dontSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL is equal to the given string. - * Unlike `seeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * seeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() - */ - public function canSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL is equal to the given string. - * Unlike `seeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * seeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() - */ - public function seeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL doesn't equal the given string. - * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * dontSeeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() - */ - public function cantSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL doesn't equal the given string. - * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * dontSeeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() - */ - public function dontSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL matches the given regular expression. - * - * ``` php - * seeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() - */ - public function canSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL matches the given regular expression. - * - * ``` php - * seeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() - */ - public function seeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url doesn't match the given regular expression. - * - * ``` php - * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() - */ - public function cantSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url doesn't match the given regular expression. - * - * ``` php - * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() - */ - public function dontSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Executes the given regular expression against the current URI and returns the first match. - * If no parameters are provided, the full URI is returned. - * - * ``` php - * grabFromCurrentUrl('~$/user/(\d+)/~'); - * $uri = $I->grabFromCurrentUrl(); - * ?> - * ``` - * - * @param null $uri - * - * @internal param $url - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() - */ - public function grabFromCurrentUrl($uri = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the specified checkbox is checked. - * - * ``` php - * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * ?> - * ``` - * - * @param $checkbox - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() - */ - public function canSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the specified checkbox is checked. - * - * ``` php - * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * ?> - * ``` - * - * @param $checkbox - * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() - */ - public function seeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that the specified checkbox is unchecked. - * - * ``` php - * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * ?> - * ``` - * - * @param $checkbox - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() - */ - public function cantSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that the specified checkbox is unchecked. - * - * ``` php - * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * ?> - * ``` - * - * @param $checkbox - * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() - */ - public function dontSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given input field or textarea contains the given value. - * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. - * - * ``` php - * seeInField('Body','Type your comment here'); - * $I->seeInField('form textarea[name=body]','Type your comment here'); - * $I->seeInField('form input[type=hidden]','hidden_value'); - * $I->seeInField('#searchform input','Search'); - * $I->seeInField('//form/*[@name=search]','Search'); - * $I->seeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInField() - */ - public function canSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given input field or textarea contains the given value. - * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. - * - * ``` php - * seeInField('Body','Type your comment here'); - * $I->seeInField('form textarea[name=body]','Type your comment here'); - * $I->seeInField('form input[type=hidden]','hidden_value'); - * $I->seeInField('#searchform input','Search'); - * $I->seeInField('//form/*[@name=search]','Search'); - * $I->seeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::seeInField() - */ - public function seeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that an input field or textarea doesn't contain the given value. - * For fuzzy locators, the field is matched by label text, CSS and XPath. - * - * ``` php - * dontSeeInField('Body','Type your comment here'); - * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); - * $I->dontSeeInField('form input[type=hidden]','hidden_value'); - * $I->dontSeeInField('#searchform input','Search'); - * $I->dontSeeInField('//form/*[@name=search]','Search'); - * $I->dontSeeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInField() - */ - public function cantSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that an input field or textarea doesn't contain the given value. - * For fuzzy locators, the field is matched by label text, CSS and XPath. - * - * ``` php - * dontSeeInField('Body','Type your comment here'); - * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); - * $I->dontSeeInField('form input[type=hidden]','hidden_value'); - * $I->dontSeeInField('#searchform input','Search'); - * $I->dontSeeInField('//form/*[@name=search]','Search'); - * $I->dontSeeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::dontSeeInField() - */ - public function dontSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function canSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function seeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function cantSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function dontSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Submits the given form on the page, optionally with the given form - * values. Give the form fields values as an array. - * - * Skipped fields will be filled by their values from the page. - * You don't need to click the 'Submit' button afterwards. - * This command itself triggers the request to form's action. - * - * You can optionally specify what button's value to include - * in the request with the last parameter as an alternative to - * explicitly setting its value in the second parameter, as - * button values are not otherwise included in the request. - * - * Examples: - * - * ``` php - * submitForm('#login', [ - * 'login' => 'davert', - * 'password' => '123456' - * ]); - * // or - * $I->submitForm('#login', [ - * 'login' => 'davert', - * 'password' => '123456' - * ], 'submitButtonName'); - * - * ``` - * - * For example, given this sample "Sign Up" form: - * - * ``` html - * - * Login: - *
- * Password: - *
- * Do you agree to out terms? - *
- * Select pricing plan: - * - * - * - * ``` - * - * You could write the following to submit it: - * - * ``` php - * submitForm( - * '#userForm', - * [ - * 'user' => [ - * 'login' => 'Davert', - * 'password' => '123456', - * 'agree' => true - * ] - * ], - * 'submitButton' - * ); - * ``` - * Note that "2" will be the submitted value for the "plan" field, as it is - * the selected option. - * - * You can also emulate a JavaScript submission by not specifying any - * buttons in the third parameter to submitForm. - * - * ```php - * submitForm( - * '#userForm', - * [ - * 'user' => [ - * 'login' => 'Davert', - * 'password' => '123456', - * 'agree' => true - * ] - * ] - * ); - * ``` - * - * Pair this with seeInFormFields for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * Parameter values can be set to arrays for multiple input fields - * of the same name, or multi-select combo boxes. For checkboxes, - * either the string value can be used, or boolean values which will - * be replaced by the checkbox's value in the DOM. - * - * ``` php - * submitForm('#my-form', [ - * 'field1' => 'value', - * 'checkbox' => [ - * 'value of first checkbox', - * 'value of second checkbox, - * ], - * 'otherCheckboxes' => [ - * true, - * false, - * false - * ], - * 'multiselect' => [ - * 'first option value', - * 'second option value' - * ] - * ]); - * ?> - * ``` - * - * Mixing string and boolean values for a checkbox's value is not supported - * and may produce unexpected results. - * - * Field names ending in "[]" must be passed without the trailing square - * bracket characters, and must contain an array for its value. This allows - * submitting multiple values with the same name, consider: - * - * ```php - * $I->submitForm('#my-form', [ - * 'field[]' => 'value', - * 'field[]' => 'another value', // 'field[]' is already a defined key - * ]); - * ``` - * - * The solution is to pass an array value: - * - * ```php - * // this way both values are submitted - * $I->submitForm('#my-form', [ - * 'field' => [ - * 'value', - * 'another value', - * ] - * ]); - * ``` - * - * @param $selector - * @param $params - * @param $button - * @see \Codeception\Lib\InnerBrowser::submitForm() - */ - public function submitForm($selector, $params, $button = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('submitForm', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Fills a text field or textarea with the given string. - * - * ``` php - * fillField("//input[@type='text']", "Hello World!"); - * $I->fillField(['name' => 'email'], 'jon@mail.com'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::fillField() - */ - public function fillField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('fillField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Selects an option in a select tag or in radio button group. - * - * ``` php - * selectOption('form select[name=account]', 'Premium'); - * $I->selectOption('form input[name=payment]', 'Monthly'); - * $I->selectOption('//form/select[@name=account]', 'Monthly'); - * ?> - * ``` - * - * Provide an array for the second argument to select multiple options: - * - * ``` php - * selectOption('Which OS do you use?', array('Windows','Linux')); - * ?> - * ``` - * - * @param $select - * @param $option - * @see \Codeception\Lib\InnerBrowser::selectOption() - */ - public function selectOption($select, $option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('selectOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Ticks a checkbox. For radio buttons, use the `selectOption` method instead. - * - * ``` php - * checkOption('#agree'); - * ?> - * ``` - * - * @param $option - * @see \Codeception\Lib\InnerBrowser::checkOption() - */ - public function checkOption($option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('checkOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Unticks a checkbox. - * - * ``` php - * uncheckOption('#notify'); - * ?> - * ``` - * - * @param $option - * @see \Codeception\Lib\InnerBrowser::uncheckOption() - */ - public function uncheckOption($option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Attaches a file relative to the Codeception data directory to the given file upload field. - * - * ``` php - * attachFile('input[@type="file"]', 'prices.xls'); - * ?> - * ``` - * - * @param $field - * @param $filename - * @see \Codeception\Lib\InnerBrowser::attachFile() - */ - public function attachFile($field, $filename) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('attachFile', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends a GET ajax request with specified params. - * - * See ->sendAjaxPostRequest for examples. - * - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest() - */ - public function sendAjaxGetRequest($uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends a POST ajax request with specified params. - * Additional params can be passed as array. - * - * Example: - * - * Imagine that by clicking checkbox you trigger ajax request which updates user settings. - * We emulate that click by running this ajax request manually. - * - * ``` php - * sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST - * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET - * - * ``` - * - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest() - */ - public function sendAjaxPostRequest($uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends an ajax request with specified method and params. - * - * Example: - * - * You need to perform an ajax request specifying the HTTP method. - * - * ``` php - * sendAjaxRequest('PUT', '/posts/7', array('title' => 'new title')); - * - * ``` - * - * @param $method - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest() - */ - public function sendAjaxRequest($method, $uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Finds and returns the text contents of the given element. - * If a fuzzy locator is used, the element is found using CSS, XPath, and by matching the full page source by regular expression. - * - * ``` php - * grabTextFrom('h1'); - * $heading = $I->grabTextFrom('descendant-or-self::h1'); - * $value = $I->grabTextFrom('~ - * ``` - * - * @param $cssOrXPathOrRegex - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabTextFrom() - */ - public function grabTextFrom($cssOrXPathOrRegex) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs the value of the given attribute value from the given element. - * Fails if element is not found. - * - * ``` php - * grabAttributeFrom('#tooltip', 'title'); - * ?> - * ``` - * - * - * @param $cssOrXpath - * @param $attribute - * @internal param $element - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() - */ - public function grabAttributeFrom($cssOrXpath, $attribute) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * - * @see \Codeception\Lib\InnerBrowser::grabMultiple() - */ - public function grabMultiple($cssOrXpath, $attribute = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabMultiple', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * @param $field - * - * @return array|mixed|null|string - * @see \Codeception\Lib\InnerBrowser::grabValueFrom() - */ - public function grabValueFrom($field) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Sets a cookie with the given name and value. - * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument. - * - * ``` php - * setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3'); - * ?> - * ``` - * - * @param $name - * @param $val - * @param array $params - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::setCookie() - */ - public function setCookie($name, $val, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs a cookie value. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabCookie() - */ - public function grabCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * ``` php - * seeCookie('PHPSESSID'); - * ?> - * ``` - * - * @param $cookie - * @param array $params - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCookie() - */ - public function canSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * ``` php - * seeCookie('PHPSESSID'); - * ?> - * ``` - * - * @param $cookie - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeCookie() - */ - public function seeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() - */ - public function cantSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() - */ - public function dontSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Unsets cookie with the given name. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::resetCookie() - */ - public function resetCookie($name, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('resetCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element exists on the page and is visible. - * You can also specify expected attributes of this element. - * - * ``` php - * seeElement('.error'); - * $I->seeElement('//form/input[1]'); - * $I->seeElement('input', ['name' => 'login']); - * $I->seeElement('input', ['value' => '123456']); - * - * // strict locator in first arg, attributes in second - * $I->seeElement(['css' => 'form input'], ['name' => 'login']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @return - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeElement() - */ - public function canSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element exists on the page and is visible. - * You can also specify expected attributes of this element. - * - * ``` php - * seeElement('.error'); - * $I->seeElement('//form/input[1]'); - * $I->seeElement('input', ['name' => 'login']); - * $I->seeElement('input', ['value' => '123456']); - * - * // strict locator in first arg, attributes in second - * $I->seeElement(['css' => 'form input'], ['name' => 'login']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @return - * @see \Codeception\Lib\InnerBrowser::seeElement() - */ - public function seeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element is invisible or not present on the page. - * You can also specify expected attributes of this element. - * - * ``` php - * dontSeeElement('.error'); - * $I->dontSeeElement('//form/input[1]'); - * $I->dontSeeElement('input', ['name' => 'login']); - * $I->dontSeeElement('input', ['value' => '123456']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeElement() - */ - public function cantSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element is invisible or not present on the page. - * You can also specify expected attributes of this element. - * - * ``` php - * dontSeeElement('.error'); - * $I->dontSeeElement('//form/input[1]'); - * $I->dontSeeElement('input', ['name' => 'login']); - * $I->dontSeeElement('input', ['value' => '123456']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @see \Codeception\Lib\InnerBrowser::dontSeeElement() - */ - public function dontSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there are a certain number of elements matched by the given locator on the page. - * - * ``` php - * seeNumberOfElements('tr', 10); - * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements - * ?> - * ``` - * @param $selector - * @param mixed $expected : - * - string: strict number - * - array: range of numbers [0,10] - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() - */ - public function canSeeNumberOfElements($selector, $expected) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberOfElements', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there are a certain number of elements matched by the given locator on the page. - * - * ``` php - * seeNumberOfElements('tr', 10); - * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements - * ?> - * ``` - * @param $selector - * @param mixed $expected : - * - string: strict number - * - array: range of numbers [0,10] - * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() - */ - public function seeNumberOfElements($selector, $expected) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeNumberOfElements', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is selected. - * - * ``` php - * seeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() - */ - public function canSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is selected. - * - * ``` php - * seeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() - */ - public function seeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is not selected. - * - * ``` php - * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() - */ - public function cantSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is not selected. - * - * ``` php - * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() - */ - public function dontSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that current page has 404 response status code. - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seePageNotFound() - */ - public function canSeePageNotFound() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that current page has 404 response status code. - * @see \Codeception\Lib\InnerBrowser::seePageNotFound() - */ - public function seePageNotFound() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * @param $code - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() - */ - public function canSeeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * @param $code - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() - */ - public function seeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title contains the given string. - * - * ``` php - * seeInTitle('Blog - Post #1'); - * ?> - * ``` - * - * @param $title - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInTitle() - */ - public function canSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title contains the given string. - * - * ``` php - * seeInTitle('Blog - Post #1'); - * ?> - * ``` - * - * @param $title - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeInTitle() - */ - public function seeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title does not contain the given string. - * - * @param $title - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() - */ - public function cantSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title does not contain the given string. - * - * @param $title - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() - */ - public function dontSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); - } -} diff --git a/tests/acceptance.suite.yml b/tests/acceptance.suite.yml deleted file mode 100644 index 3f02674248..0000000000 --- a/tests/acceptance.suite.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Codeception Test Suite Configuration -# -# Suite for acceptance tests. -# Perform tests in browser using the WebDriver or PhpBrowser. -# If you need both WebDriver and PHPBrowser tests - create a separate suite. - -class_name: AcceptanceTester -modules: - enabled: - - PhpBrowser: - url: http://localhost/myapp - - \Helper\Acceptance diff --git a/tests/acceptance/_bootstrap.php b/tests/acceptance/_bootstrap.php deleted file mode 100644 index 8a88555806..0000000000 --- a/tests/acceptance/_bootstrap.php +++ /dev/null @@ -1,2 +0,0 @@ - Date: Fri, 8 Jan 2016 14:28:15 +0100 Subject: [PATCH 006/276] Updated app file after upgrading to laravel 5.2 --- bootstrap/app.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) mode change 100644 => 100755 bootstrap/app.php diff --git a/bootstrap/app.php b/bootstrap/app.php old mode 100644 new mode 100755 index 3eef0b49d7..ebd712abe1 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,9 +11,8 @@ | */ - $app = new Illuminate\Foundation\Application( - realpath(__DIR__ . '/../') + realpath(__DIR__.'/../') ); /* @@ -27,23 +26,21 @@ $app = new Illuminate\Foundation\Application( | */ - $app->singleton( - 'Illuminate\Contracts\Http\Kernel', - 'FireflyIII\Http\Kernel' + Illuminate\Contracts\Http\Kernel::class, + FireflyIII\Http\Kernel::class ); $app->singleton( - 'Illuminate\Contracts\Console\Kernel', - 'FireflyIII\Console\Kernel' + Illuminate\Contracts\Console\Kernel::class, + FireflyIII\Console\Kernel::class ); $app->singleton( - 'Illuminate\Contracts\Debug\ExceptionHandler', - 'FireflyIII\Exceptions\Handler' + Illuminate\Contracts\Debug\ExceptionHandler::class, + FireflyIII\Exceptions\Handler::class ); - /* |-------------------------------------------------------------------------- | Return The Application From f7c50a123a916bc0d080cff3b23a067d92198ba1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:28:59 +0100 Subject: [PATCH 007/276] Updated config file after upgrading to laravel 5.2 --- config/app.php | 169 ++++++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 80 deletions(-) mode change 100644 => 100755 config/app.php diff --git a/config/app.php b/config/app.php old mode 100644 new mode 100755 index ced4a3e0b6..58ba0b91ce --- a/config/app.php +++ b/config/app.php @@ -2,6 +2,19 @@ return [ + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services your application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + /* |-------------------------------------------------------------------------- | Application Debug Mode @@ -13,7 +26,7 @@ return [ | */ - 'debug' => env('APP_DEBUG'), + 'debug' => env('APP_DEBUG', false), /* |-------------------------------------------------------------------------- @@ -26,7 +39,7 @@ return [ | */ - 'url' => 'http://localhost', + 'url' => 'http://localhost', /* |-------------------------------------------------------------------------- @@ -39,7 +52,7 @@ return [ | */ - 'timezone' => 'UTC', + 'timezone' => 'UTC', /* |-------------------------------------------------------------------------- @@ -52,7 +65,7 @@ return [ | */ - 'locale' => 'en_US', + 'locale' => 'en_US', /* |-------------------------------------------------------------------------- @@ -78,9 +91,9 @@ return [ | */ - 'key' => env('APP_KEY', 'SomeRandomString'), + 'key' => env('APP_KEY'), - 'cipher' => MCRYPT_RIJNDAEL_128, + 'cipher' => 'AES-256-CBC', /* |-------------------------------------------------------------------------- @@ -95,7 +108,7 @@ return [ | */ - 'log' => 'daily', + 'log' => env('APP_LOG', 'daily'), /* |-------------------------------------------------------------------------- @@ -108,52 +121,48 @@ return [ | */ - 'providers' => [ + 'providers' => [ /* * Laravel Framework Service Providers... */ - 'Illuminate\Foundation\Providers\ArtisanServiceProvider', - 'Illuminate\Auth\AuthServiceProvider', - //'Illuminate\Bus\BusServiceProvider', - 'Illuminate\Cache\CacheServiceProvider', - 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider', - 'Illuminate\Routing\ControllerServiceProvider', - 'Illuminate\Cookie\CookieServiceProvider', - 'Illuminate\Database\DatabaseServiceProvider', - 'Illuminate\Encryption\EncryptionServiceProvider', - 'Illuminate\Filesystem\FilesystemServiceProvider', - 'Illuminate\Foundation\Providers\FoundationServiceProvider', - 'Illuminate\Hashing\HashServiceProvider', - 'Illuminate\Mail\MailServiceProvider', - 'Illuminate\Pagination\PaginationServiceProvider', - 'Illuminate\Pipeline\PipelineServiceProvider', - 'Illuminate\Queue\QueueServiceProvider', - 'Illuminate\Redis\RedisServiceProvider', - 'Illuminate\Auth\Passwords\PasswordResetServiceProvider', - 'Illuminate\Session\SessionServiceProvider', - 'Illuminate\Translation\TranslationServiceProvider', - 'Illuminate\Validation\ValidationServiceProvider', - 'Illuminate\View\ViewServiceProvider', - 'Illuminate\Html\HtmlServiceProvider', - 'TwigBridge\ServiceProvider', + Illuminate\Auth\AuthServiceProvider::class, + Illuminate\Broadcasting\BroadcastServiceProvider::class, + Illuminate\Bus\BusServiceProvider::class, + Illuminate\Cache\CacheServiceProvider::class, + Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Database\DatabaseServiceProvider::class, + Illuminate\Encryption\EncryptionServiceProvider::class, + Illuminate\Filesystem\FilesystemServiceProvider::class, + Illuminate\Foundation\Providers\FoundationServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Mail\MailServiceProvider::class, + Illuminate\Pagination\PaginationServiceProvider::class, + Illuminate\Pipeline\PipelineServiceProvider::class, + Illuminate\Queue\QueueServiceProvider::class, + Illuminate\Redis\RedisServiceProvider::class, + Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + Illuminate\Translation\TranslationServiceProvider::class, + Illuminate\Validation\ValidationServiceProvider::class, + Illuminate\View\ViewServiceProvider::class, + Collective\Html\HtmlServiceProvider::class, - 'DaveJamesMiller\Breadcrumbs\ServiceProvider', -// 'Barryvdh\Debugbar\ServiceProvider', -// 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', - 'Zizaco\Entrust\EntrustServiceProvider', /* * Application Service Providers... */ - 'FireflyIII\Providers\AppServiceProvider', - 'FireflyIII\Providers\BusServiceProvider', - 'FireflyIII\Providers\ConfigServiceProvider', - 'FireflyIII\Providers\EventServiceProvider', - 'FireflyIII\Providers\RouteServiceProvider', - 'FireflyIII\Providers\FireflyServiceProvider', - 'FireflyIII\Providers\TestingServiceProvider', + FireflyIII\Providers\AppServiceProvider::class, + FireflyIII\Providers\AuthServiceProvider::class, + FireflyIII\Providers\EventServiceProvider::class, + FireflyIII\Providers\RouteServiceProvider::class, + FireflyIII\Providers\FireflyServiceProvider::class, + // own stuff: + Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + 'DaveJamesMiller\Breadcrumbs\ServiceProvider', + 'TwigBridge\ServiceProvider', ], @@ -168,50 +177,50 @@ return [ | */ - 'aliases' => [ + 'aliases' => [ - 'App' => 'Illuminate\Support\Facades\App', - 'Artisan' => 'Illuminate\Support\Facades\Artisan', - 'Auth' => 'Illuminate\Support\Facades\Auth', - 'Blade' => 'Illuminate\Support\Facades\Blade', - 'Bus' => 'Illuminate\Support\Facades\Bus', - 'Cache' => 'Illuminate\Support\Facades\Cache', - 'Config' => 'Illuminate\Support\Facades\Config', - 'Cookie' => 'Illuminate\Support\Facades\Cookie', - 'Crypt' => 'Illuminate\Support\Facades\Crypt', - 'DB' => 'Illuminate\Support\Facades\DB', - 'Eloquent' => 'Illuminate\Database\Eloquent\Model', - 'Event' => 'Illuminate\Support\Facades\Event', - 'File' => 'Illuminate\Support\Facades\File', - 'Hash' => 'Illuminate\Support\Facades\Hash', - 'Input' => 'Illuminate\Support\Facades\Input', - 'Inspiring' => 'Illuminate\Foundation\Inspiring', - 'Lang' => 'Illuminate\Support\Facades\Lang', - 'Log' => 'Illuminate\Support\Facades\Log', - 'Mail' => 'Illuminate\Support\Facades\Mail', - 'Password' => 'Illuminate\Support\Facades\Password', - 'Queue' => 'Illuminate\Support\Facades\Queue', - 'Redirect' => 'Illuminate\Support\Facades\Redirect', - 'Redis' => 'Illuminate\Support\Facades\Redis', - 'Request' => 'Illuminate\Support\Facades\Request', - 'Response' => 'Illuminate\Support\Facades\Response', - 'Route' => 'Illuminate\Support\Facades\Route', - 'Schema' => 'Illuminate\Support\Facades\Schema', - 'Session' => 'Illuminate\Support\Facades\Session', - 'Storage' => 'Illuminate\Support\Facades\Storage', - 'URL' => 'Illuminate\Support\Facades\URL', - 'Validator' => 'Illuminate\Support\Facades\Validator', - 'View' => 'Illuminate\Support\Facades\View', - 'Form' => 'Illuminate\Html\FormFacade', - 'Html' => 'Illuminate\Html\HtmlFacade', + 'App' => Illuminate\Support\Facades\App::class, + 'Artisan' => Illuminate\Support\Facades\Artisan::class, + 'Auth' => Illuminate\Support\Facades\Auth::class, + 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Cache' => Illuminate\Support\Facades\Cache::class, + 'Config' => Illuminate\Support\Facades\Config::class, + 'Cookie' => Illuminate\Support\Facades\Cookie::class, + 'Crypt' => Illuminate\Support\Facades\Crypt::class, + 'DB' => Illuminate\Support\Facades\DB::class, + 'Eloquent' => Illuminate\Database\Eloquent\Model::class, + 'Event' => Illuminate\Support\Facades\Event::class, + 'File' => Illuminate\Support\Facades\File::class, + 'Gate' => Illuminate\Support\Facades\Gate::class, + 'Hash' => Illuminate\Support\Facades\Hash::class, + + 'Lang' => Illuminate\Support\Facades\Lang::class, + 'Log' => Illuminate\Support\Facades\Log::class, + 'Mail' => Illuminate\Support\Facades\Mail::class, + 'Password' => Illuminate\Support\Facades\Password::class, + 'Queue' => Illuminate\Support\Facades\Queue::class, + 'Redirect' => Illuminate\Support\Facades\Redirect::class, + 'Redis' => Illuminate\Support\Facades\Redis::class, + 'Request' => Illuminate\Support\Facades\Request::class, + 'Response' => Illuminate\Support\Facades\Response::class, + 'Route' => Illuminate\Support\Facades\Route::class, + 'Schema' => Illuminate\Support\Facades\Schema::class, + 'Session' => Illuminate\Support\Facades\Session::class, + 'Storage' => Illuminate\Support\Facades\Storage::class, + 'URL' => Illuminate\Support\Facades\URL::class, + 'Validator' => Illuminate\Support\Facades\Validator::class, + 'View' => Illuminate\Support\Facades\View::class, + 'Twig' => 'TwigBridge\Facade\Twig', + 'Form' => Collective\Html\FormFacade::class, + 'Html' => Collective\Html\HtmlFacade::class, 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', 'Preferences' => 'FireflyIII\Support\Facades\Preferences', 'Navigation' => 'FireflyIII\Support\Facades\Navigation', 'Amount' => 'FireflyIII\Support\Facades\Amount', 'Steam' => 'FireflyIII\Support\Facades\Steam', 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', - 'Twig' => 'TwigBridge\Facade\Twig', - 'Entrust' => 'Zizaco\Entrust\EntrustFacade' + 'Entrust' => 'Zizaco\Entrust\EntrustFacade', + 'Input' => 'Illuminate\Support\Facades\Input', ], From 78a7b995d20fbda71c3d7d63329d84859e004fe5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:29:12 +0100 Subject: [PATCH 008/276] Updated artisan after upgrading to laravel 5.2 --- artisan | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 artisan diff --git a/artisan b/artisan old mode 100644 new mode 100755 index eb5e2bb62d..df630d0d6d --- a/artisan +++ b/artisan @@ -28,11 +28,11 @@ $app = require_once __DIR__.'/bootstrap/app.php'; | */ -$kernel = $app->make('Illuminate\Contracts\Console\Kernel'); +$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); $status = $kernel->handle( - $input = new Symfony\Component\Console\Input\ArgvInput, - new Symfony\Component\Console\Output\ConsoleOutput + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput ); /* From 4b00db7662c873597cdc275b1b4d428a9b81ba22 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 14:30:19 +0100 Subject: [PATCH 009/276] Removed test code after upgrading to laravel 5.2 (will have to reinstate). --- tests/TestCase.php | 40 +- tests/_support/FunctionalTester.php | 26 - tests/_support/Helper/Functional.php | 14 - tests/_support/Helper/Unit.php | 14 - tests/_support/UnitTester.php | 26 - .../_generated/FunctionalTesterActions.php | 23 - .../_support/_generated/UnitTesterActions.php | 2756 ----------------- tests/functional.suite.yml | 11 - tests/unit.suite.yml | 10 - tests/unit/Models/TransactionTypeTest.php | 32 - 10 files changed, 7 insertions(+), 2945 deletions(-) mode change 100644 => 100755 tests/TestCase.php delete mode 100644 tests/_support/FunctionalTester.php delete mode 100644 tests/_support/Helper/Functional.php delete mode 100644 tests/_support/Helper/Unit.php delete mode 100644 tests/_support/UnitTester.php delete mode 100644 tests/_support/_generated/FunctionalTesterActions.php delete mode 100644 tests/_support/_generated/UnitTesterActions.php delete mode 100644 tests/functional.suite.yml delete mode 100644 tests/unit.suite.yml delete mode 100644 tests/unit/Models/TransactionTypeTest.php diff --git a/tests/TestCase.php b/tests/TestCase.php old mode 100644 new mode 100755 index c5a49310ed..8578b17e4a --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,10 +1,12 @@ make('Illuminate\Contracts\Console\Kernel')->bootstrap(); + $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); return $app; } - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * This method is called before the first test of this test class is run. - * - * @since Method available since Release 3.4.0 - */ - public static function setUpBeforeClass() - { - parent::setUpBeforeClass(); - } - - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - public function tearDown() - { - parent::tearDown(); - } } diff --git a/tests/_support/FunctionalTester.php b/tests/_support/FunctionalTester.php deleted file mode 100644 index 8f9d5f234a..0000000000 --- a/tests/_support/FunctionalTester.php +++ /dev/null @@ -1,26 +0,0 @@ -getScenario()->runStep(new \Codeception\Step\Action('getApplication', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Opens web page using route name and parameters. - * - * ``` php - * amOnRoute('posts.create'); - * ?> - * ``` - * - * @param $routeName - * @param array $params - * @see \Codeception\Module\Laravel5::amOnRoute() - */ - public function amOnRoute($routeName, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnRoute', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Opens web page by action name - * - * ``` php - * amOnAction('PostsController@index'); - * ?> - * ``` - * - * @param $action - * @param array $params - * @see \Codeception\Module\Laravel5::amOnAction() - */ - public function amOnAction($action, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnAction', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches route - * - * ``` php - * seeCurrentRouteIs('posts.index'); - * ?> - * ``` - * @param $route - * @param array $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() - */ - public function canSeeCurrentRouteIs($route, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentRouteIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches route - * - * ``` php - * seeCurrentRouteIs('posts.index'); - * ?> - * ``` - * @param $route - * @param array $params - * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() - */ - public function seeCurrentRouteIs($route, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentRouteIs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches action - * - * ``` php - * seeCurrentActionIs('PostsController@index'); - * ?> - * ``` - * - * @param $action - * @param array $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeCurrentActionIs() - */ - public function canSeeCurrentActionIs($action, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentActionIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches action - * - * ``` php - * seeCurrentActionIs('PostsController@index'); - * ?> - * ``` - * - * @param $action - * @param array $params - * @see \Codeception\Module\Laravel5::seeCurrentActionIs() - */ - public function seeCurrentActionIs($action, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentActionIs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that a session variable exists. - * - * ``` php - * seeInSession('key'); - * $I->seeInSession('key', 'value'); - * ?> - * ``` - * - * @param string|array $key - * @param mixed $value - * @return void - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeInSession() - */ - public function canSeeInSession($key, $value = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInSession', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that a session variable exists. - * - * ``` php - * seeInSession('key'); - * $I->seeInSession('key', 'value'); - * ?> - * ``` - * - * @param string|array $key - * @param mixed $value - * @return void - * @see \Codeception\Module\Laravel5::seeInSession() - */ - public function seeInSession($key, $value = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInSession', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that the session has a given list of values. - * - * ``` php - * seeSessionHasValues(['key1', 'key2']); - * $I->seeSessionHasValues(['key1' => 'value1', 'key2' => 'value2']); - * ?> - * ``` - * - * @param array $bindings - * @return void - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeSessionHasValues() - */ - public function canSeeSessionHasValues($bindings) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeSessionHasValues', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that the session has a given list of values. - * - * ``` php - * seeSessionHasValues(['key1', 'key2']); - * $I->seeSessionHasValues(['key1' => 'value1', 'key2' => 'value2']); - * ?> - * ``` - * - * @param array $bindings - * @return void - * @see \Codeception\Module\Laravel5::seeSessionHasValues() - */ - public function seeSessionHasValues($bindings) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeSessionHasValues', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that form errors are bound to the View. - * - * ``` php - * seeFormHasErrors(); - * ?> - * ``` - * - * @return bool - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeFormHasErrors() - */ - public function canSeeFormHasErrors() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFormHasErrors', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that form errors are bound to the View. - * - * ``` php - * seeFormHasErrors(); - * ?> - * ``` - * - * @return bool - * @see \Codeception\Module\Laravel5::seeFormHasErrors() - */ - public function seeFormHasErrors() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFormHasErrors', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that specific form error messages are set in the view. - * - * Useful for validation messages e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage - * - * ``` php - * seeFormErrorMessages(array('username'=>'Invalid Username')); - * ?> - * ``` - * @param array $bindings - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeFormErrorMessages() - */ - public function canSeeFormErrorMessages($bindings) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFormErrorMessages', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that specific form error messages are set in the view. - * - * Useful for validation messages e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage - * - * ``` php - * seeFormErrorMessages(array('username'=>'Invalid Username')); - * ?> - * ``` - * @param array $bindings - * @see \Codeception\Module\Laravel5::seeFormErrorMessages() - */ - public function seeFormErrorMessages($bindings) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFormErrorMessages', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that specific form error message is set in the view. - * - * Useful for validation messages and generally messages array - * e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage - * - * ``` php - * seeFormErrorMessage('username', 'Invalid Username'); - * ?> - * ``` - * @param string $key - * @param string $errorMessage - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeFormErrorMessage() - */ - public function canSeeFormErrorMessage($key, $errorMessage) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFormErrorMessage', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Assert that specific form error message is set in the view. - * - * Useful for validation messages and generally messages array - * e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage - * - * ``` php - * seeFormErrorMessage('username', 'Invalid Username'); - * ?> - * ``` - * @param string $key - * @param string $errorMessage - * @see \Codeception\Module\Laravel5::seeFormErrorMessage() - */ - public function seeFormErrorMessage($key, $errorMessage) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFormErrorMessage', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Set the currently logged in user for the application. - * Takes either an object that implements the User interface or - * an array of credentials. - * - * @param \Illuminate\Contracts\Auth\User|array $user - * @param string $driver - * @return void - * @see \Codeception\Module\Laravel5::amLoggedAs() - */ - public function amLoggedAs($user, $driver = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amLoggedAs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Logs user out - * @see \Codeception\Module\Laravel5::logout() - */ - public function logout() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('logout', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that user is authenticated - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeAuthentication() - */ - public function canSeeAuthentication() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeAuthentication', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that user is authenticated - * @see \Codeception\Module\Laravel5::seeAuthentication() - */ - public function seeAuthentication() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeAuthentication', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that user is not authenticated - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::dontSeeAuthentication() - */ - public function cantSeeAuthentication() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeAuthentication', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that user is not authenticated - * @see \Codeception\Module\Laravel5::dontSeeAuthentication() - */ - public function dontSeeAuthentication() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeAuthentication', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Return an instance of a class from the IoC Container. - * (http://laravel.com/docs/ioc) - * - * Example - * ``` php - * grabService('foo'); - * - * // Will return an instance of FooBar, also works for singletons. - * ?> - * ``` - * - * @param string $class - * @return mixed - * @see \Codeception\Module\Laravel5::grabService() - */ - public function grabService($class) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabService', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Inserts record into the database. - * - * ``` php - * haveRecord('users', array('name' => 'Davert')); - * ?> - * ``` - * - * @param $tableName - * @param array $attributes - * @return mixed - * @part orm - * @see \Codeception\Module\Laravel5::haveRecord() - */ - public function haveRecord($tableName, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('haveRecord', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that record exists in database. - * - * ``` php - * seeRecord('users', array('name' => 'davert')); - * ?> - * ``` - * - * @param $tableName - * @param array $attributes - * @part orm - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeRecord() - */ - public function canSeeRecord($tableName, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeRecord', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that record exists in database. - * - * ``` php - * seeRecord('users', array('name' => 'davert')); - * ?> - * ``` - * - * @param $tableName - * @param array $attributes - * @part orm - * @see \Codeception\Module\Laravel5::seeRecord() - */ - public function seeRecord($tableName, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeRecord', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that record does not exist in database. - * - * ``` php - * dontSeeRecord('users', array('name' => 'davert')); - * ?> - * ``` - * - * @param $tableName - * @param array $attributes - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::dontSeeRecord() - */ - public function cantSeeRecord($tableName, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeRecord', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that record does not exist in database. - * - * ``` php - * dontSeeRecord('users', array('name' => 'davert')); - * ?> - * ``` - * - * @param $tableName - * @param array $attributes - * @see \Codeception\Module\Laravel5::dontSeeRecord() - */ - public function dontSeeRecord($tableName, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeRecord', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Retrieves record from database - * - * ``` php - * grabRecord('users', array('name' => 'davert')); - * ?> - * ``` - * - * @param $tableName - * @param array $attributes - * @return mixed - * @part orm - * @see \Codeception\Module\Laravel5::grabRecord() - */ - public function grabRecord($tableName, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabRecord', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Authenticates user for HTTP_AUTH - * - * @param $username - * @param $password - * @see \Codeception\Lib\InnerBrowser::amHttpAuthenticated() - */ - public function amHttpAuthenticated($username, $password) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Opens the page for the given relative URI. - * - * ``` php - * amOnPage('/'); - * // opens /register page - * $I->amOnPage('/register'); - * ?> - * ``` - * - * @param $page - * @see \Codeception\Lib\InnerBrowser::amOnPage() - */ - public function amOnPage($page) { - return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Perform a click on a link or a button, given by a locator. - * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. - * For buttons, the "value" attribute, "name" attribute, and inner text are searched. - * For links, the link text is searched. - * For images, the "alt" attribute and inner text of any parent links are searched. - * - * The second parameter is a context (CSS or XPath locator) to narrow the search. - * - * Note that if the locator matches a button of type `submit`, the form will be submitted. - * - * ``` php - * click('Logout'); - * // button of form - * $I->click('Submit'); - * // CSS button - * $I->click('#form input[type=submit]'); - * // XPath - * $I->click('//form/*[@type=submit]'); - * // link in context - * $I->click('Logout', '#nav'); - * // using strict locator - * $I->click(['link' => 'Login']); - * ?> - * ``` - * - * @param $link - * @param $context - * @see \Codeception\Lib\InnerBrowser::click() - */ - public function click($link, $context = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('click', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. - * - * ``` php - * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::see() - */ - public function canSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. - * - * ``` php - * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * @see \Codeception\Lib\InnerBrowser::see() - */ - public function see($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('see', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page doesn't contain the text specified. - * Give a locator as the second parameter to match a specific region. - * - * ```php - * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSee() - */ - public function cantSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page doesn't contain the text specified. - * Give a locator as the second parameter to match a specific region. - * - * ```php - * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> - * ``` - * - * @param $text - * @param null $selector - * @see \Codeception\Lib\InnerBrowser::dontSee() - */ - public function dontSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there's a link with the specified text. - * Give a full URL as the second parameter to match links with that exact URL. - * - * ``` php - * seeLink('Logout'); // matches Logout - * $I->seeLink('Logout','/logout'); // matches Logout - * ?> - * ``` - * - * @param $text - * @param null $url - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeLink() - */ - public function canSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there's a link with the specified text. - * Give a full URL as the second parameter to match links with that exact URL. - * - * ``` php - * seeLink('Logout'); // matches Logout - * $I->seeLink('Logout','/logout'); // matches Logout - * ?> - * ``` - * - * @param $text - * @param null $url - * @see \Codeception\Lib\InnerBrowser::seeLink() - */ - public function seeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page doesn't contain a link with the given string. - * If the second parameter is given, only links with a matching "href" attribute will be checked. - * - * ``` php - * dontSeeLink('Logout'); // I suppose user is not logged in - * $I->dontSeeLink('Checkout now', '/store/cart.php'); - * ?> - * ``` - * - * @param $text - * @param null $url - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeLink() - */ - public function cantSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page doesn't contain a link with the given string. - * If the second parameter is given, only links with a matching "href" attribute will be checked. - * - * ``` php - * dontSeeLink('Logout'); // I suppose user is not logged in - * $I->dontSeeLink('Checkout now', '/store/cart.php'); - * ?> - * ``` - * - * @param $text - * @param null $url - * @see \Codeception\Lib\InnerBrowser::dontSeeLink() - */ - public function dontSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current URI contains the given string. - * - * ``` php - * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() - */ - public function canSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current URI contains the given string. - * - * ``` php - * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() - */ - public function seeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URI doesn't contain the given string. - * - * ``` php - * dontSeeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() - */ - public function cantSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URI doesn't contain the given string. - * - * ``` php - * dontSeeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() - */ - public function dontSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL is equal to the given string. - * Unlike `seeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * seeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() - */ - public function canSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL is equal to the given string. - * Unlike `seeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * seeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() - */ - public function seeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL doesn't equal the given string. - * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * dontSeeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() - */ - public function cantSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL doesn't equal the given string. - * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * dontSeeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() - */ - public function dontSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL matches the given regular expression. - * - * ``` php - * seeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() - */ - public function canSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL matches the given regular expression. - * - * ``` php - * seeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() - */ - public function seeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url doesn't match the given regular expression. - * - * ``` php - * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() - */ - public function cantSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url doesn't match the given regular expression. - * - * ``` php - * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> - * ``` - * - * @param $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() - */ - public function dontSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Executes the given regular expression against the current URI and returns the first match. - * If no parameters are provided, the full URI is returned. - * - * ``` php - * grabFromCurrentUrl('~$/user/(\d+)/~'); - * $uri = $I->grabFromCurrentUrl(); - * ?> - * ``` - * - * @param null $uri - * - * @internal param $url - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() - */ - public function grabFromCurrentUrl($uri = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the specified checkbox is checked. - * - * ``` php - * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * ?> - * ``` - * - * @param $checkbox - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() - */ - public function canSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the specified checkbox is checked. - * - * ``` php - * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * ?> - * ``` - * - * @param $checkbox - * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() - */ - public function seeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that the specified checkbox is unchecked. - * - * ``` php - * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * ?> - * ``` - * - * @param $checkbox - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() - */ - public function cantSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that the specified checkbox is unchecked. - * - * ``` php - * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * ?> - * ``` - * - * @param $checkbox - * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() - */ - public function dontSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given input field or textarea contains the given value. - * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. - * - * ``` php - * seeInField('Body','Type your comment here'); - * $I->seeInField('form textarea[name=body]','Type your comment here'); - * $I->seeInField('form input[type=hidden]','hidden_value'); - * $I->seeInField('#searchform input','Search'); - * $I->seeInField('//form/*[@name=search]','Search'); - * $I->seeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInField() - */ - public function canSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given input field or textarea contains the given value. - * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. - * - * ``` php - * seeInField('Body','Type your comment here'); - * $I->seeInField('form textarea[name=body]','Type your comment here'); - * $I->seeInField('form input[type=hidden]','hidden_value'); - * $I->seeInField('#searchform input','Search'); - * $I->seeInField('//form/*[@name=search]','Search'); - * $I->seeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::seeInField() - */ - public function seeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that an input field or textarea doesn't contain the given value. - * For fuzzy locators, the field is matched by label text, CSS and XPath. - * - * ``` php - * dontSeeInField('Body','Type your comment here'); - * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); - * $I->dontSeeInField('form input[type=hidden]','hidden_value'); - * $I->dontSeeInField('#searchform input','Search'); - * $I->dontSeeInField('//form/*[@name=search]','Search'); - * $I->dontSeeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInField() - */ - public function cantSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that an input field or textarea doesn't contain the given value. - * For fuzzy locators, the field is matched by label text, CSS and XPath. - * - * ``` php - * dontSeeInField('Body','Type your comment here'); - * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); - * $I->dontSeeInField('form input[type=hidden]','hidden_value'); - * $I->dontSeeInField('#searchform input','Search'); - * $I->dontSeeInField('//form/*[@name=search]','Search'); - * $I->dontSeeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::dontSeeInField() - */ - public function dontSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function canSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function seeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function cantSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function dontSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Submits the given form on the page, optionally with the given form - * values. Give the form fields values as an array. - * - * Skipped fields will be filled by their values from the page. - * You don't need to click the 'Submit' button afterwards. - * This command itself triggers the request to form's action. - * - * You can optionally specify what button's value to include - * in the request with the last parameter as an alternative to - * explicitly setting its value in the second parameter, as - * button values are not otherwise included in the request. - * - * Examples: - * - * ``` php - * submitForm('#login', [ - * 'login' => 'davert', - * 'password' => '123456' - * ]); - * // or - * $I->submitForm('#login', [ - * 'login' => 'davert', - * 'password' => '123456' - * ], 'submitButtonName'); - * - * ``` - * - * For example, given this sample "Sign Up" form: - * - * ``` html - *
- * Login: - *
- * Password: - *
- * Do you agree to out terms? - *
- * Select pricing plan: - * - * - *
- * ``` - * - * You could write the following to submit it: - * - * ``` php - * submitForm( - * '#userForm', - * [ - * 'user' => [ - * 'login' => 'Davert', - * 'password' => '123456', - * 'agree' => true - * ] - * ], - * 'submitButton' - * ); - * ``` - * Note that "2" will be the submitted value for the "plan" field, as it is - * the selected option. - * - * You can also emulate a JavaScript submission by not specifying any - * buttons in the third parameter to submitForm. - * - * ```php - * submitForm( - * '#userForm', - * [ - * 'user' => [ - * 'login' => 'Davert', - * 'password' => '123456', - * 'agree' => true - * ] - * ] - * ); - * ``` - * - * Pair this with seeInFormFields for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * Parameter values can be set to arrays for multiple input fields - * of the same name, or multi-select combo boxes. For checkboxes, - * either the string value can be used, or boolean values which will - * be replaced by the checkbox's value in the DOM. - * - * ``` php - * submitForm('#my-form', [ - * 'field1' => 'value', - * 'checkbox' => [ - * 'value of first checkbox', - * 'value of second checkbox, - * ], - * 'otherCheckboxes' => [ - * true, - * false, - * false - * ], - * 'multiselect' => [ - * 'first option value', - * 'second option value' - * ] - * ]); - * ?> - * ``` - * - * Mixing string and boolean values for a checkbox's value is not supported - * and may produce unexpected results. - * - * Field names ending in "[]" must be passed without the trailing square - * bracket characters, and must contain an array for its value. This allows - * submitting multiple values with the same name, consider: - * - * ```php - * $I->submitForm('#my-form', [ - * 'field[]' => 'value', - * 'field[]' => 'another value', // 'field[]' is already a defined key - * ]); - * ``` - * - * The solution is to pass an array value: - * - * ```php - * // this way both values are submitted - * $I->submitForm('#my-form', [ - * 'field' => [ - * 'value', - * 'another value', - * ] - * ]); - * ``` - * - * @param $selector - * @param $params - * @param $button - * @see \Codeception\Lib\InnerBrowser::submitForm() - */ - public function submitForm($selector, $params, $button = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('submitForm', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Fills a text field or textarea with the given string. - * - * ``` php - * fillField("//input[@type='text']", "Hello World!"); - * $I->fillField(['name' => 'email'], 'jon@mail.com'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::fillField() - */ - public function fillField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('fillField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Selects an option in a select tag or in radio button group. - * - * ``` php - * selectOption('form select[name=account]', 'Premium'); - * $I->selectOption('form input[name=payment]', 'Monthly'); - * $I->selectOption('//form/select[@name=account]', 'Monthly'); - * ?> - * ``` - * - * Provide an array for the second argument to select multiple options: - * - * ``` php - * selectOption('Which OS do you use?', array('Windows','Linux')); - * ?> - * ``` - * - * @param $select - * @param $option - * @see \Codeception\Lib\InnerBrowser::selectOption() - */ - public function selectOption($select, $option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('selectOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Ticks a checkbox. For radio buttons, use the `selectOption` method instead. - * - * ``` php - * checkOption('#agree'); - * ?> - * ``` - * - * @param $option - * @see \Codeception\Lib\InnerBrowser::checkOption() - */ - public function checkOption($option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('checkOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Unticks a checkbox. - * - * ``` php - * uncheckOption('#notify'); - * ?> - * ``` - * - * @param $option - * @see \Codeception\Lib\InnerBrowser::uncheckOption() - */ - public function uncheckOption($option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Attaches a file relative to the Codeception data directory to the given file upload field. - * - * ``` php - * attachFile('input[@type="file"]', 'prices.xls'); - * ?> - * ``` - * - * @param $field - * @param $filename - * @see \Codeception\Lib\InnerBrowser::attachFile() - */ - public function attachFile($field, $filename) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('attachFile', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends a GET ajax request with specified params. - * - * See ->sendAjaxPostRequest for examples. - * - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest() - */ - public function sendAjaxGetRequest($uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends a POST ajax request with specified params. - * Additional params can be passed as array. - * - * Example: - * - * Imagine that by clicking checkbox you trigger ajax request which updates user settings. - * We emulate that click by running this ajax request manually. - * - * ``` php - * sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST - * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET - * - * ``` - * - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest() - */ - public function sendAjaxPostRequest($uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends an ajax request with specified method and params. - * - * Example: - * - * You need to perform an ajax request specifying the HTTP method. - * - * ``` php - * sendAjaxRequest('PUT', '/posts/7', array('title' => 'new title')); - * - * ``` - * - * @param $method - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest() - */ - public function sendAjaxRequest($method, $uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Finds and returns the text contents of the given element. - * If a fuzzy locator is used, the element is found using CSS, XPath, and by matching the full page source by regular expression. - * - * ``` php - * grabTextFrom('h1'); - * $heading = $I->grabTextFrom('descendant-or-self::h1'); - * $value = $I->grabTextFrom('~ - * ``` - * - * @param $cssOrXPathOrRegex - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabTextFrom() - */ - public function grabTextFrom($cssOrXPathOrRegex) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs the value of the given attribute value from the given element. - * Fails if element is not found. - * - * ``` php - * grabAttributeFrom('#tooltip', 'title'); - * ?> - * ``` - * - * - * @param $cssOrXpath - * @param $attribute - * @internal param $element - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() - */ - public function grabAttributeFrom($cssOrXpath, $attribute) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * - * @see \Codeception\Lib\InnerBrowser::grabMultiple() - */ - public function grabMultiple($cssOrXpath, $attribute = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabMultiple', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * @param $field - * - * @return array|mixed|null|string - * @see \Codeception\Lib\InnerBrowser::grabValueFrom() - */ - public function grabValueFrom($field) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Sets a cookie with the given name and value. - * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument. - * - * ``` php - * setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3'); - * ?> - * ``` - * - * @param $name - * @param $val - * @param array $params - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::setCookie() - */ - public function setCookie($name, $val, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs a cookie value. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabCookie() - */ - public function grabCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * ``` php - * seeCookie('PHPSESSID'); - * ?> - * ``` - * - * @param $cookie - * @param array $params - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCookie() - */ - public function canSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * ``` php - * seeCookie('PHPSESSID'); - * ?> - * ``` - * - * @param $cookie - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeCookie() - */ - public function seeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() - */ - public function cantSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() - */ - public function dontSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Unsets cookie with the given name. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::resetCookie() - */ - public function resetCookie($name, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('resetCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element exists on the page and is visible. - * You can also specify expected attributes of this element. - * - * ``` php - * seeElement('.error'); - * $I->seeElement('//form/input[1]'); - * $I->seeElement('input', ['name' => 'login']); - * $I->seeElement('input', ['value' => '123456']); - * - * // strict locator in first arg, attributes in second - * $I->seeElement(['css' => 'form input'], ['name' => 'login']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @return - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeElement() - */ - public function canSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element exists on the page and is visible. - * You can also specify expected attributes of this element. - * - * ``` php - * seeElement('.error'); - * $I->seeElement('//form/input[1]'); - * $I->seeElement('input', ['name' => 'login']); - * $I->seeElement('input', ['value' => '123456']); - * - * // strict locator in first arg, attributes in second - * $I->seeElement(['css' => 'form input'], ['name' => 'login']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @return - * @see \Codeception\Lib\InnerBrowser::seeElement() - */ - public function seeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element is invisible or not present on the page. - * You can also specify expected attributes of this element. - * - * ``` php - * dontSeeElement('.error'); - * $I->dontSeeElement('//form/input[1]'); - * $I->dontSeeElement('input', ['name' => 'login']); - * $I->dontSeeElement('input', ['value' => '123456']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeElement() - */ - public function cantSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element is invisible or not present on the page. - * You can also specify expected attributes of this element. - * - * ``` php - * dontSeeElement('.error'); - * $I->dontSeeElement('//form/input[1]'); - * $I->dontSeeElement('input', ['name' => 'login']); - * $I->dontSeeElement('input', ['value' => '123456']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @see \Codeception\Lib\InnerBrowser::dontSeeElement() - */ - public function dontSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there are a certain number of elements matched by the given locator on the page. - * - * ``` php - * seeNumberOfElements('tr', 10); - * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements - * ?> - * ``` - * @param $selector - * @param mixed $expected : - * - string: strict number - * - array: range of numbers [0,10] - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() - */ - public function canSeeNumberOfElements($selector, $expected) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberOfElements', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there are a certain number of elements matched by the given locator on the page. - * - * ``` php - * seeNumberOfElements('tr', 10); - * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements - * ?> - * ``` - * @param $selector - * @param mixed $expected : - * - string: strict number - * - array: range of numbers [0,10] - * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() - */ - public function seeNumberOfElements($selector, $expected) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeNumberOfElements', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is selected. - * - * ``` php - * seeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() - */ - public function canSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is selected. - * - * ``` php - * seeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() - */ - public function seeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is not selected. - * - * ``` php - * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() - */ - public function cantSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is not selected. - * - * ``` php - * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() - */ - public function dontSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that current page has 404 response status code. - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seePageNotFound() - */ - public function canSeePageNotFound() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that current page has 404 response status code. - * @see \Codeception\Lib\InnerBrowser::seePageNotFound() - */ - public function seePageNotFound() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * @param $code - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() - */ - public function canSeeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * @param $code - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() - */ - public function seeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title contains the given string. - * - * ``` php - * seeInTitle('Blog - Post #1'); - * ?> - * ``` - * - * @param $title - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInTitle() - */ - public function canSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title contains the given string. - * - * ``` php - * seeInTitle('Blog - Post #1'); - * ?> - * ``` - * - * @param $title - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeInTitle() - */ - public function seeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title does not contain the given string. - * - * @param $title - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() - */ - public function cantSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title does not contain the given string. - * - * @param $title - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() - */ - public function dontSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that two variables are equal. - * - * @param $expected - * @param $actual - * @param string $message - * - * @return mixed - * @see \Codeception\Module\Asserts::assertEquals() - */ - public function assertEquals($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that two variables are not equal - * - * @param $expected - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertNotEquals() - */ - public function assertNotEquals($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that two variables are same - * - * @param $expected - * @param $actual - * @param string $message - * - * @return mixed - * @see \Codeception\Module\Asserts::assertSame() - */ - public function assertSame($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertSame', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that two variables are not same - * - * @param $expected - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertNotSame() - */ - public function assertNotSame($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotSame', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that actual is greater than expected - * - * @param $expected - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertGreaterThan() - */ - public function assertGreaterThan($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThan', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * @deprecated - * @see \Codeception\Module\Asserts::assertGreaterThen() - */ - public function assertGreaterThen($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThen', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that actual is greater or equal than expected - * - * @param $expected - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertGreaterThanOrEqual() - */ - public function assertGreaterThanOrEqual($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThanOrEqual', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * @deprecated - * @see \Codeception\Module\Asserts::assertGreaterThenOrEqual() - */ - public function assertGreaterThenOrEqual($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThenOrEqual', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that actual is less than expected - * - * @param $expected - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertLessThan() - */ - public function assertLessThan($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThan', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that actual is less or equal than expected - * - * @param $expected - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertLessThanOrEqual() - */ - public function assertLessThanOrEqual($expected, $actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThanOrEqual', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that haystack contains needle - * - * @param $needle - * @param $haystack - * @param string $message - * @see \Codeception\Module\Asserts::assertContains() - */ - public function assertContains($needle, $haystack, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContains', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that haystack doesn't contain needle. - * - * @param $needle - * @param $haystack - * @param string $message - * @see \Codeception\Module\Asserts::assertNotContains() - */ - public function assertNotContains($needle, $haystack, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContains', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that string match with pattern - * - * @param string $pattern - * @param string $string - * @param string $message - * @see \Codeception\Module\Asserts::assertRegExp() - */ - public function assertRegExp($pattern, $string, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertRegExp', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that string not match with pattern - * - * @param string $pattern - * @param string $string - * @param string $message - * @see \Codeception\Module\Asserts::assertNotRegExp() - */ - public function assertNotRegExp($pattern, $string, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotRegExp', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that variable is empty. - * - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertEmpty() - */ - public function assertEmpty($actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEmpty', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that variable is not empty. - * - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertNotEmpty() - */ - public function assertNotEmpty($actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEmpty', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that variable is NULL - * - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertNull() - */ - public function assertNull($actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNull', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that variable is not NULL - * - * @param $actual - * @param string $message - * @see \Codeception\Module\Asserts::assertNotNull() - */ - public function assertNotNull($actual, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotNull', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that condition is positive. - * - * @param $condition - * @param string $message - * @see \Codeception\Module\Asserts::assertTrue() - */ - public function assertTrue($condition, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertTrue', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that condition is negative. - * - * @param $condition - * @param string $message - * @see \Codeception\Module\Asserts::assertFalse() - */ - public function assertFalse($condition, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFalse', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if file exists - * - * @param string $filename - * @param string $message - * @see \Codeception\Module\Asserts::assertFileExists() - */ - public function assertFileExists($filename, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileExists', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if file doesn't exists - * - * @param string $filename - * @param string $message - * @see \Codeception\Module\Asserts::assertFileNotExists() - */ - public function assertFileNotExists($filename, $message = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotExists', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Fails the test with message. - * - * @param $message - * @see \Codeception\Module\Asserts::fail() - */ - public function fail($message) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('fail', func_get_args())); - } -} diff --git a/tests/functional.suite.yml b/tests/functional.suite.yml deleted file mode 100644 index f94a08d7ad..0000000000 --- a/tests/functional.suite.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Codeception Test Suite Configuration -# -# Suite for functional (integration) tests -# Emulate web requests and make application process them -# Include one of framework modules (Symfony2, Yii2, Laravel5) to use it - -class_name: FunctionalTester -modules: - enabled: - # add framework module here - - \Helper\Functional diff --git a/tests/unit.suite.yml b/tests/unit.suite.yml deleted file mode 100644 index ad6e55bd0e..0000000000 --- a/tests/unit.suite.yml +++ /dev/null @@ -1,10 +0,0 @@ -# Codeception Test Suite Configuration -# -# Suite for unit (internal) tests. - -class_name: UnitTester -modules: - enabled: - - Laravel5 - - Asserts - - \Helper\Unit diff --git a/tests/unit/Models/TransactionTypeTest.php b/tests/unit/Models/TransactionTypeTest.php deleted file mode 100644 index a93005ae6f..0000000000 --- a/tests/unit/Models/TransactionTypeTest.php +++ /dev/null @@ -1,32 +0,0 @@ -first(); - $this->assertTrue($transactionType->isWithdrawal()); - } - - public function testIsDeposit() - { - $transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - $this->assertTrue($transactionType->isDeposit()); - } - - public function testIsTransfer() - { - $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); - $this->assertTrue($transactionType->isTransfer()); - } - - public function testIsOpeningBalance() - { - $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); - $this->assertTrue($transactionType->isOpeningBalance()); - } -} From c7b47b4453cf1b7a4c61f0b2b0f572f7cdf7ed99 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 15:55:22 +0100 Subject: [PATCH 010/276] Fix view after upgrade to Laravel 5.2 --- resources/views/reports/index.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 93ca24c200..d27cca0e24 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -131,7 +131,7 @@ {% block scripts %} From 180ec527980cb8ba25f9120a9d51c7cb2a2a2141 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 15:56:35 +0100 Subject: [PATCH 011/276] Various new files after upgrade to Laravel 5.2 --- database/factories/ModelFactory.php | 21 +++++++++++++++++++++ public/index.php | 13 ++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100755 database/factories/ModelFactory.php mode change 100644 => 100755 public/index.php diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php new file mode 100755 index 0000000000..14ee71d02a --- /dev/null +++ b/database/factories/ModelFactory.php @@ -0,0 +1,21 @@ +define(FireflyIII\User::class, function (Faker\Generator $faker) { + return [ + 'name' => $faker->name, + 'email' => $faker->email, + 'password' => bcrypt(str_random(10)), + 'remember_token' => str_random(10), + ]; +}); diff --git a/public/index.php b/public/index.php old mode 100644 new mode 100755 index 94d706ee68..c5820533bc --- a/public/index.php +++ b/public/index.php @@ -1,4 +1,5 @@ make('Illuminate\Contracts\Http\Kernel'); +$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() @@ -55,5 +55,4 @@ $response = $kernel->handle( $response->send(); - $kernel->terminate($request, $response); From 013e16e15f6f790199edafba5789fd89c3a8ca5f Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 15:59:21 +0100 Subject: [PATCH 012/276] Updated config after upgrade to Laravel 5.2 --- config/auth.php | 115 +++++++++++++++++++++++++++------------- config/breadcrumbs.php | 7 --- config/broadcasting.php | 52 ++++++++++++++++++ config/cache.php | 28 +++++----- config/compile.php | 10 +--- config/database.php | 21 ++++---- config/filesystems.php | 27 +++++++--- config/mail.php | 28 +++------- config/queue.php | 36 ++++++------- config/services.php | 21 ++++---- config/session.php | 71 ++++++++++++++++++++----- config/view.php | 6 +-- 12 files changed, 271 insertions(+), 151 deletions(-) mode change 100644 => 100755 config/auth.php delete mode 100644 config/breadcrumbs.php create mode 100755 config/broadcasting.php mode change 100644 => 100755 config/cache.php mode change 100644 => 100755 config/compile.php mode change 100644 => 100755 config/database.php mode change 100644 => 100755 config/filesystems.php mode change 100644 => 100755 config/mail.php mode change 100644 => 100755 config/queue.php mode change 100644 => 100755 config/services.php mode change 100644 => 100755 config/session.php mode change 100644 => 100755 config/view.php diff --git a/config/auth.php b/config/auth.php old mode 100644 new mode 100755 index 685437114c..46cc20e0eb --- a/config/auth.php +++ b/config/auth.php @@ -4,65 +4,104 @@ return [ /* |-------------------------------------------------------------------------- - | Default Authentication Driver + | Authentication Defaults |-------------------------------------------------------------------------- | - | This option controls the authentication driver that will be utilized. - | This driver manages the retrieval and authentication of the users - | attempting to get access to protected areas of your application. + | This option controls the default authentication "guard" and password + | reset options for your application. You may change these defaults + | as required, but they're a perfect start for most applications. + | + */ + + 'defaults' => [ + 'guard' => 'web', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session", "token" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + + 'api' => [ + 'driver' => 'token', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */ - 'driver' => 'eloquent', + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => FireflyIII\User::class, + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], /* |-------------------------------------------------------------------------- - | Authentication Model - |-------------------------------------------------------------------------- - | - | When using the "Eloquent" authentication driver, we need to know which - | Eloquent model should be used to retrieve your users. Of course, it - | is often just the "User" model but you may use whatever you like. - | - */ - - 'model' => 'FireflyIII\User', - - /* - |-------------------------------------------------------------------------- - | Authentication Table - |-------------------------------------------------------------------------- - | - | When using the "Database" authentication driver, we need to know which - | table should be used to retrieve your users. We have chosen a basic - | default value but you may easily change it to any table you like. - | - */ - - 'table' => 'users', - - /* - |-------------------------------------------------------------------------- - | Password Reset Settings + | Resetting Passwords |-------------------------------------------------------------------------- | | Here you may set the options for resetting passwords including the view - | that is your password reset e-mail. You can also set the name of the + | that is your password reset e-mail. You may also set the name of the | table that maintains all of the reset tokens for your application. | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | | The expire time is the number of minutes that the reset token should be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */ - 'password' => [ - 'email' => 'emails.password', - 'table' => 'password_resets', - 'expire' => 60, + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'email' => 'auth.emails.password', + 'table' => 'password_resets', + 'expire' => 60, + ], ], - 'allow_register' => true ]; diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php deleted file mode 100644 index aa133d355c..0000000000 --- a/config/breadcrumbs.php +++ /dev/null @@ -1,7 +0,0 @@ - 'breadcrumbs::bootstrap3', - -]; diff --git a/config/broadcasting.php b/config/broadcasting.php new file mode 100755 index 0000000000..abaaac32a7 --- /dev/null +++ b/config/broadcasting.php @@ -0,0 +1,52 @@ + env('BROADCAST_DRIVER', 'pusher'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_KEY'), + 'secret' => env('PUSHER_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + // + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + 'log' => [ + 'driver' => 'log', + ], + + ], + +]; diff --git a/config/cache.php b/config/cache.php old mode 100644 new mode 100755 index 0a2d218517..379135b0eb --- a/config/cache.php +++ b/config/cache.php @@ -26,38 +26,38 @@ return [ | */ - 'stores' => [ + 'stores' => [ - 'apc' => [ - 'driver' => 'apc' + 'apc' => [ + 'driver' => 'apc', ], - 'array' => [ - 'driver' => 'array' + 'array' => [ + 'driver' => 'array', ], - 'database' => [ - 'driver' => 'database', - 'table' => 'cache', + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', 'connection' => null, ], - 'file' => [ + 'file' => [ 'driver' => 'file', - 'path' => storage_path() . '/framework/cache', + 'path' => storage_path('framework/cache'), ], 'memcached' => [ 'driver' => 'memcached', 'servers' => [ [ - 'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100 + 'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100, ], ], ], - 'redis' => [ - 'driver' => 'redis', + 'redis' => [ + 'driver' => 'redis', 'connection' => 'default', ], @@ -74,6 +74,6 @@ return [ | */ - 'prefix' => 'laravel', + 'prefix' => 'laravel', ]; diff --git a/config/compile.php b/config/compile.php old mode 100644 new mode 100755 index 317efeb092..04807eac45 --- a/config/compile.php +++ b/config/compile.php @@ -13,14 +13,8 @@ return [ | */ - 'files' => [ - - realpath(__DIR__ . '/../app/Providers/AppServiceProvider.php'), - realpath(__DIR__ . '/../app/Providers/BusServiceProvider.php'), - realpath(__DIR__ . '/../app/Providers/ConfigServiceProvider.php'), - realpath(__DIR__ . '/../app/Providers/EventServiceProvider.php'), - realpath(__DIR__ . '/../app/Providers/RouteServiceProvider.php'), - + 'files' => [ + // ], /* diff --git a/config/database.php b/config/database.php old mode 100644 new mode 100755 index f1a98b129b..66e88a9090 --- a/config/database.php +++ b/config/database.php @@ -13,7 +13,7 @@ return [ | */ - 'fetch' => PDO::FETCH_CLASS, + 'fetch' => PDO::FETCH_CLASS, /* |-------------------------------------------------------------------------- @@ -26,8 +26,7 @@ return [ | */ - 'default' => env('DB_CONNECTION', 'mysql'), - + 'default' => env('DB_CONNECTION', 'mysql'), /* |-------------------------------------------------------------------------- @@ -49,11 +48,11 @@ return [ 'sqlite' => [ 'driver' => 'sqlite', - 'database' => storage_path('database/testing.db'), + 'database' => database_path('database.sqlite'), 'prefix' => '', ], - 'mysql' => [ + 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), @@ -65,7 +64,7 @@ return [ 'strict' => false, ], - 'pgsql' => [ + 'pgsql' => [ 'driver' => 'pgsql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), @@ -82,6 +81,7 @@ return [ 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', 'prefix' => '', ], @@ -98,7 +98,7 @@ return [ | */ - 'migrations' => 'migrations', + 'migrations' => 'migrations', /* |-------------------------------------------------------------------------- @@ -111,13 +111,14 @@ return [ | */ - 'redis' => [ + 'redis' => [ 'cluster' => false, 'default' => [ - 'host' => '127.0.0.1', - 'port' => 6379, + 'host' => env('REDIS_HOST', 'localhost'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), 'database' => 0, ], diff --git a/config/filesystems.php b/config/filesystems.php old mode 100644 new mode 100755 index 9765bd1016..3fffcf0a2f --- a/config/filesystems.php +++ b/config/filesystems.php @@ -11,7 +11,7 @@ return [ | by the framework. A "local" driver, as well as a variety of cloud | based drivers are available for your choosing. Just store away! | - | Supported: "local", "s3", "rackspace" + | Supported: "local", "ftp", "s3", "rackspace" | */ @@ -28,7 +28,7 @@ return [ | */ - 'cloud' => 's3', + 'cloud' => 's3', /* |-------------------------------------------------------------------------- @@ -41,14 +41,28 @@ return [ | */ - 'disks' => [ + 'disks' => [ - 'local' => [ + 'local' => [ 'driver' => 'local', - 'root' => storage_path() . '/app', + 'root' => storage_path('app'), ], - 's3' => [ + 'ftp' => [ + 'driver' => 'ftp', + 'host' => 'ftp.example.com', + 'username' => 'your-username', + 'password' => 'your-password', + + // Optional FTP Settings... + // 'port' => 21, + // 'root' => '', + // 'passive' => true, + // 'ssl' => true, + // 'timeout' => 30, + ], + + 's3' => [ 'driver' => 's3', 'key' => 'your-key', 'secret' => 'your-secret', @@ -63,6 +77,7 @@ return [ 'container' => 'your-container', 'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/', 'region' => 'IAD', + 'url_type' => 'publicURL', ], ], diff --git a/config/mail.php b/config/mail.php old mode 100644 new mode 100755 index a89c2eaad6..fa74f98ae8 --- a/config/mail.php +++ b/config/mail.php @@ -11,12 +11,11 @@ return [ | sending of e-mail. You may specify which one you're using throughout | your application here. By default, Laravel is setup for SMTP mail. | - | Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill", "log" + | Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill", "ses", "log" | */ - 'blocked_domains' => explode(',', env('BLOCKED_DOMAINS')), - 'driver' => env('EMAIL_DRIVER', 'smtp'), + 'driver' => env('MAIL_DRIVER', 'smtp'), /* |-------------------------------------------------------------------------- @@ -29,7 +28,7 @@ return [ | */ - 'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'), + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), /* |-------------------------------------------------------------------------- @@ -42,7 +41,7 @@ return [ | */ - 'port' => 587, + 'port' => env('MAIL_PORT', 587), /* |-------------------------------------------------------------------------- @@ -68,7 +67,7 @@ return [ | */ - 'encryption' => 'tls', + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), /* |-------------------------------------------------------------------------- @@ -81,7 +80,7 @@ return [ | */ - 'username' => env('EMAIL_USERNAME', null), + 'username' => env('MAIL_USERNAME'), /* |-------------------------------------------------------------------------- @@ -94,7 +93,7 @@ return [ | */ - 'password' => env('EMAIL_PASSWORD', null), + 'password' => env('MAIL_PASSWORD'), /* |-------------------------------------------------------------------------- @@ -109,17 +108,4 @@ return [ 'sendmail' => '/usr/sbin/sendmail -bs', - /* - |-------------------------------------------------------------------------- - | Mail "Pretend" - |-------------------------------------------------------------------------- - | - | When this option is enabled, e-mail will not actually be sent over the - | web and will instead be written to your application's logs files so - | you may inspect the message. This is great for local development. - | - */ - - 'pretend' => env('EMAIL_PRETEND', false), - ]; diff --git a/config/queue.php b/config/queue.php old mode 100644 new mode 100755 index 5be8d63bf3..6c2b7d2e17 --- a/config/queue.php +++ b/config/queue.php @@ -12,11 +12,11 @@ return [ | syntax for each one. Here you may set the default queue driver. | | Supported: "null", "sync", "database", "beanstalkd", - | "sqs", "iron", "redis" + | "sqs", "redis" | */ - 'default' => env('QUEUE_DRIVER', 'sync'), + 'default' => env('QUEUE_DRIVER', 'sync'), /* |-------------------------------------------------------------------------- @@ -31,11 +31,11 @@ return [ 'connections' => [ - 'sync' => [ + 'sync' => [ 'driver' => 'sync', ], - 'database' => [ + 'database' => [ 'driver' => 'database', 'table' => 'jobs', 'queue' => 'default', @@ -49,27 +49,20 @@ return [ 'ttr' => 60, ], - 'sqs' => [ + 'sqs' => [ 'driver' => 'sqs', 'key' => 'your-public-key', 'secret' => 'your-secret-key', - 'queue' => 'your-queue-url', + 'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id', + 'queue' => 'your-queue-name', 'region' => 'us-east-1', ], - 'iron' => [ - 'driver' => 'iron', - 'host' => 'mq-aws-us-east-1.iron.io', - 'token' => 'your-token', - 'project' => 'your-project-id', - 'queue' => 'your-queue-name', - 'encrypt' => true, - ], - - 'redis' => [ - 'driver' => 'redis', - 'queue' => 'default', - 'expire' => 60, + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => 'default', + 'expire' => 60, ], ], @@ -85,8 +78,9 @@ return [ | */ - 'failed' => [ - 'database' => 'mysql', 'table' => 'failed_jobs', + 'failed' => [ + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', ], ]; diff --git a/config/services.php b/config/services.php old mode 100644 new mode 100755 index 3ee3eed75e..9fbb81549e --- a/config/services.php +++ b/config/services.php @@ -14,24 +14,25 @@ return [ | */ - 'mailgun' => [ - 'domain' => '', - 'secret' => '', + 'mailgun' => [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), ], 'mandrill' => [ - 'secret' => '', + 'secret' => env('MANDRILL_SECRET'), ], - 'ses' => [ - 'key' => '', - 'secret' => '', + 'ses' => [ + 'key' => env('SES_KEY'), + 'secret' => env('SES_SECRET'), 'region' => 'us-east-1', ], - 'stripe' => [ - 'model' => 'User', - 'secret' => '', + 'stripe' => [ + 'model' => FireflyIII\User::class, + 'key' => env('STRIPE_KEY'), + 'secret' => env('STRIPE_SECRET'), ], ]; diff --git a/config/session.php b/config/session.php old mode 100644 new mode 100755 index 17aed45faf..69aac40867 --- a/config/session.php +++ b/config/session.php @@ -2,6 +2,22 @@ return [ + /* + |-------------------------------------------------------------------------- + | Default Session Driver + |-------------------------------------------------------------------------- + | + | This option controls the default session "driver" that will be used on + | requests. By default, we will use the lightweight native driver but + | you may specify any of the other wonderful drivers provided here. + | + | Supported: "file", "cookie", "database", "apc", + | "memcached", "redis", "array" + | + */ + + 'driver' => env('SESSION_DRIVER', 'file'), + /* |-------------------------------------------------------------------------- | Session Lifetime @@ -13,10 +29,22 @@ return [ | */ - 'lifetime' => 120, + 'lifetime' => 120, 'expire_on_close' => false, + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => true, /* |-------------------------------------------------------------------------- @@ -29,8 +57,20 @@ return [ | */ - 'files' => storage_path() . '/framework/sessions', + 'files' => storage_path('framework/sessions'), + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => null, /* |-------------------------------------------------------------------------- @@ -43,7 +83,7 @@ return [ | */ - 'table' => 'sessions', + 'table' => 'sessions', /* |-------------------------------------------------------------------------- @@ -56,15 +96,20 @@ return [ | */ - 'lottery' => [2, 100], + 'lottery' => [2, 100], - 'driver' => env('SESSION_DRIVER', 'database'), - 'cookie' => 'firefly_session', + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ - 'connection' => env('DB_CONNECTION', 'mysql'), - - - 'encrypt' => true, + 'cookie' => 'firefly_session', /* |-------------------------------------------------------------------------- @@ -77,7 +122,7 @@ return [ | */ - 'path' => '/', + 'path' => '/', /* |-------------------------------------------------------------------------- @@ -90,7 +135,7 @@ return [ | */ - 'domain' => null, + 'domain' => null, /* |-------------------------------------------------------------------------- @@ -103,6 +148,6 @@ return [ | */ - 'secure' => false, + 'secure' => true, ]; diff --git a/config/view.php b/config/view.php old mode 100644 new mode 100755 index 3ea032617f..e193ab61d9 --- a/config/view.php +++ b/config/view.php @@ -13,8 +13,8 @@ return [ | */ - 'paths' => [ - realpath(base_path('resources/twig')) + 'paths' => [ + realpath(base_path('resources/views')), ], /* @@ -28,6 +28,6 @@ return [ | */ - 'compiled' => realpath(storage_path() . '/framework/views'), + 'compiled' => realpath(storage_path('framework/views')), ]; From 9b03e6b1243f8a03c0e0ad7f148c7d74728e2bc7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 15:59:30 +0100 Subject: [PATCH 013/276] Updated config after upgrade to Laravel 5.2 --- bootstrap/autoload.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 bootstrap/autoload.php diff --git a/bootstrap/autoload.php b/bootstrap/autoload.php old mode 100644 new mode 100755 index 0f75195105..383013796f --- a/bootstrap/autoload.php +++ b/bootstrap/autoload.php @@ -14,7 +14,7 @@ define('LARAVEL_START', microtime(true)); | */ -require __DIR__ . '/../vendor/autoload.php'; +require __DIR__.'/../vendor/autoload.php'; /* |-------------------------------------------------------------------------- @@ -27,7 +27,7 @@ require __DIR__ . '/../vendor/autoload.php'; | */ -$compiledPath = __DIR__ . '/cache/compiled.php'; +$compiledPath = __DIR__.'/cache/compiled.php'; if (file_exists($compiledPath)) { require $compiledPath; From 7e4b9af315134993de14091487376be270c10539 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:00:07 +0100 Subject: [PATCH 014/276] Updated providers after upgrade to Laravel 5.2 --- app/Providers/AppServiceProvider.php | 23 ++--------- app/Providers/AuthServiceProvider.php | 31 +++++++++++++++ app/Providers/BusServiceProvider.php | 44 --------------------- app/Providers/ConfigServiceProvider.php | 30 -------------- app/Providers/EventServiceProvider.php | 17 ++++---- app/Providers/RouteServiceProvider.php | 32 +++++---------- app/Providers/TestingServiceProvider.php | 29 -------------- app/Services/Registrar.php | 50 ------------------------ 8 files changed, 52 insertions(+), 204 deletions(-) mode change 100644 => 100755 app/Providers/AppServiceProvider.php create mode 100755 app/Providers/AuthServiceProvider.php delete mode 100644 app/Providers/BusServiceProvider.php delete mode 100644 app/Providers/ConfigServiceProvider.php mode change 100644 => 100755 app/Providers/EventServiceProvider.php mode change 100644 => 100755 app/Providers/RouteServiceProvider.php delete mode 100644 app/Providers/TestingServiceProvider.php delete mode 100644 app/Services/Registrar.php diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php old mode 100644 new mode 100755 index 68afc940c3..73d79bc734 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -1,17 +1,11 @@ -app->bind( - 'Illuminate\Contracts\Auth\Registrar', - 'FireflyIII\Services\Registrar' - ); - + // } - } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php new file mode 100755 index 0000000000..da063968ec --- /dev/null +++ b/app/Providers/AuthServiceProvider.php @@ -0,0 +1,31 @@ + 'FireflyIII\Policies\ModelPolicy', + ]; + + /** + * Register any application authentication / authorization services. + * + * @param \Illuminate\Contracts\Auth\Access\Gate $gate + * @return void + */ + public function boot(GateContract $gate) + { + $this->registerPolicies($gate); + + // + } +} diff --git a/app/Providers/BusServiceProvider.php b/app/Providers/BusServiceProvider.php deleted file mode 100644 index dc2e86ac21..0000000000 --- a/app/Providers/BusServiceProvider.php +++ /dev/null @@ -1,44 +0,0 @@ -mapUsing( - function ($command) { - return Dispatcher::simpleMapping( - $command, 'FireflyIII\Commands', 'FireflyIII\Handlers\Commands' - ); - } - ); - } - - /** - * Register any application services. - * - * @return void - */ - public function register() - { - // - } - -} diff --git a/app/Providers/ConfigServiceProvider.php b/app/Providers/ConfigServiceProvider.php deleted file mode 100644 index 7ee3b11047..0000000000 --- a/app/Providers/ConfigServiceProvider.php +++ /dev/null @@ -1,30 +0,0 @@ -group( - ['namespace' => $this->namespace], function ($router) { - /** @noinspection PhpIncludeInspection */ + $router->group(['namespace' => $this->namespace], function ($router) { require app_path('Http/routes.php'); - } - ); + }); } - } diff --git a/app/Providers/TestingServiceProvider.php b/app/Providers/TestingServiceProvider.php deleted file mode 100644 index a548521e04..0000000000 --- a/app/Providers/TestingServiceProvider.php +++ /dev/null @@ -1,29 +0,0 @@ -app->environment() == 'testing') { - $this->app['config']['session.driver'] = 'native'; - } - } - -} diff --git a/app/Services/Registrar.php b/app/Services/Registrar.php deleted file mode 100644 index 5f69c7ba24..0000000000 --- a/app/Services/Registrar.php +++ /dev/null @@ -1,50 +0,0 @@ - $data['email'], - 'password' => $data['password'], - ] - ); - } - - /** - * Get a validator for an incoming registration request. - * - * @param array $data - * - * @return \Illuminate\Contracts\Validation\Validator - */ - public function validator(array $data) - { - return Validator::make( - $data, [ - 'email' => 'required|email|max:255|unique:users', - 'password' => 'required|confirmed|min:6', - ] - ); - } - -} From c360cc6db6c10e6c829abe4510b314b6ae83fdd5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:00:16 +0100 Subject: [PATCH 015/276] Update jobs after upgrade to Laravel 5.2 --- app/Jobs/Job.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 app/Jobs/Job.php diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php new file mode 100755 index 0000000000..82f3af1c2c --- /dev/null +++ b/app/Jobs/Job.php @@ -0,0 +1,21 @@ + Date: Fri, 8 Jan 2016 16:00:28 +0100 Subject: [PATCH 016/276] Update requests after upgrade to Laravel 5.2 --- app/Http/Requests/Request.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) mode change 100644 => 100755 app/Http/Requests/Request.php diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php old mode 100644 new mode 100755 index 73c38578b3..3bc8daac17 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -1,16 +1,10 @@ - Date: Fri, 8 Jan 2016 16:00:57 +0100 Subject: [PATCH 017/276] Update middleware after upgrade to Laravel 5.2 --- app/Http/Middleware/Authenticate.php | 52 ++++------------- app/Http/Middleware/EncryptCookies.php | 17 ++++++ app/Http/Middleware/Range.php | 2 +- .../Middleware/RedirectIfAuthenticated.php | 43 ++++---------- app/Http/Middleware/ReplaceTestVars.php | 57 ------------------- app/Http/Middleware/VerifyCsrfToken.php | 28 +++------ 6 files changed, 47 insertions(+), 152 deletions(-) mode change 100644 => 100755 app/Http/Middleware/Authenticate.php create mode 100755 app/Http/Middleware/EncryptCookies.php mode change 100644 => 100755 app/Http/Middleware/RedirectIfAuthenticated.php delete mode 100644 app/Http/Middleware/ReplaceTestVars.php diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php old mode 100644 new mode 100755 index 5cc1d665de..e7424954b4 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -1,65 +1,33 @@ -auth = $auth; - } - /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next + * @param string|null $guard * * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle($request, Closure $next, $guard = null) { - - if ($this->auth->guest()) { + if (Auth::guard($guard)->guest()) { if ($request->ajax()) { return response('Unauthorized.', 401); } else { - return redirect()->guest('auth/login'); - } - } - /** @var User $user */ - $user = $this->auth->user(); - if ($user instanceof User && intval($user->blocked) == 1) { - Auth::logout(); - return redirect()->route('index'); + return redirect()->guest('login'); + } } // if logged in, set user language: @@ -72,7 +40,7 @@ class Authenticate setlocale(LC_TIME, $locale); setlocale(LC_MONETARY, $locale); + return $next($request); } - } diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php new file mode 100755 index 0000000000..d863b91383 --- /dev/null +++ b/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,17 @@ +auth->check()) { diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php old mode 100644 new mode 100755 index b5419be62c..5fbd2d05b2 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -1,52 +1,29 @@ -auth = $auth; - } - /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next + * @param string|null $guard * * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle($request, Closure $next, $guard = null) { - if ($this->auth->check()) { - return new RedirectResponse(url('/')); + if (Auth::guard($guard)->check()) { + + return redirect('/'); } return $next($request); } - } diff --git a/app/Http/Middleware/ReplaceTestVars.php b/app/Http/Middleware/ReplaceTestVars.php deleted file mode 100644 index 5e90f0253c..0000000000 --- a/app/Http/Middleware/ReplaceTestVars.php +++ /dev/null @@ -1,57 +0,0 @@ -app = $app; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * - * @return mixed - */ - public function handle(Request $request, Closure $next) - { - if ('testing' === $this->app->environment() && $request->has('_token')) { - $input = $request->all(); - $input['_token'] = $request->session()->token(); - // we need to update _token value to make sure we get the POST / PUT tests passed. - Log::debug('Input token replaced (' . $input['_token'] . ').'); - $request->replace($input); - } - - return $next($request); - } - -} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 71dd254d3d..89a57ce341 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -1,27 +1,17 @@ - Date: Fri, 8 Jan 2016 16:01:21 +0100 Subject: [PATCH 018/276] Update http related classes after upgrade to Laravel 5.2 --- app/Http/Controllers/Auth/AuthController.php | 201 ++++++++++-------- .../Controllers/Auth/PasswordController.php | 29 +-- app/Http/Controllers/Controller.php | 25 ++- app/Http/Kernel.php | 53 +++-- app/Http/routes.php | 17 +- 5 files changed, 179 insertions(+), 146 deletions(-) mode change 100644 => 100755 app/Http/Controllers/Auth/AuthController.php mode change 100644 => 100755 app/Http/Controllers/Auth/PasswordController.php mode change 100644 => 100755 app/Http/Controllers/Controller.php mode change 100644 => 100755 app/Http/Kernel.php diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php old mode 100644 new mode 100755 index c4f5ba4f9b..26510cf6a3 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -1,4 +1,6 @@ -middleware('guest', ['except' => 'logout']); } /** @@ -42,13 +58,14 @@ class AuthController extends Controller * * @return \Illuminate\Http\Response */ - public function getRegister() + public function showRegistrationForm() { $host = Rq::getHttpHost(); return view('auth.register', compact('host')); } + /** * Handle a login request to the application. * @@ -56,9 +73,17 @@ class AuthController extends Controller * * @return \Illuminate\Http\Response */ - public function postLogin(Request $request) + public function login(Request $request) { - $this->validate($request, [$this->loginUsername() => 'required', 'password' => 'required',]); + $this->validate( + $request, [ + $this->loginUsername() => 'required', 'password' => 'required', + ] + ); + + // If the class is using the ThrottlesLogins trait, we can automatically throttle + // the login attempts for this application. We'll key this by the username and + // the IP address of the client making these requests into this application. $throttles = $this->isUsingThrottlesLoginsTrait(); if ($throttles && $this->hasTooManyLoginAttempts($request)) { @@ -68,67 +93,112 @@ class AuthController extends Controller $credentials = $this->getCredentials($request); $credentials['blocked'] = 0; // most not be blocked. - if (Auth::attempt($credentials, $request->has('remember'))) { + if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) { return $this->handleUserWasAuthenticated($request, $throttles); } - $message = $this->getFailedLoginMessage(); + // check if user is blocked: + $message = ''; /** @var User $foundUser */ $foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first(); if (!is_null($foundUser)) { // if it exists, show message: $code = $foundUser->blocked_code; + if (strlen($code) == 0) { $code = 'general_blocked'; } $message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]); } + + // If the login attempt was unsuccessful we will increment the number of attempts + // to login and redirect the user back to the login form. Of course, when this + // user surpasses their maximum number of attempts they will get locked out. if ($throttles) { $this->incrementLoginAttempts($request); } - return redirect($this->loginPath()) - ->withInput($request->only($this->loginUsername(), 'remember')) - ->withErrors([$this->loginUsername() => $message,]); - } - - - public $redirectTo = '/'; - - /** - * Create a new authentication controller instance. - * - * @codeCoverageIgnore - * - */ - public function __construct() - { - parent::__construct(); - - $this->middleware('guest', ['except' => 'getLogout']); + return $this->sendFailedLoginResponse($request, $message); } /** - * Show the application login form. + * Get the failed login response instance. + * + * @param \Illuminate\Http\Request $request * - * @codeCoverageIgnore * @return \Illuminate\Http\Response - * */ - public function getLogin() + protected function sendFailedLoginResponse(Request $request, $message) { - return Twig::render('auth.login'); + return redirect()->back() + ->withInput($request->only($this->loginUsername(), 'remember')) + ->withErrors( + [ + $this->loginUsername() => $this->getFailedLoginMessage($message), + ] + ); + } + + /** + * Get the failed login message. + * + * @return string + */ + protected function getFailedLoginMessage($message) + { + if (strlen($message) > 0) { + return $message; + } + + return Lang::has('auth.failed') + ? Lang::get('auth.failed') + : 'These credentials do not match our records.'; + } + + + /** + * Get a validator for an incoming registration request. + * + * @param array $data + * + * @return \Illuminate\Contracts\Validation\Validator + */ + protected function validator(array $data) + { + return Validator::make( + $data, [ + 'email' => 'required|email|max:255|unique:users', + 'password' => 'required|confirmed|min:6', + ] + ); + } + + /** + * Create a new user instance after a valid registration. + * + * @param array $data + * + * @return User + */ + protected function create(array $data) + { + return User::create( + [ + 'email' => $data['email'], + 'password' => bcrypt($data['password']), + ] + ); } /** * Handle a registration request for the application. * - * @param Request $request + * @param \Illuminate\Http\Request $request * - * @return \Illuminate\Http\RedirectResponse + * @return \Illuminate\Http\Response */ - public function postRegister(Request $request) + public function register(Request $request) { $validator = $this->validator($request->all()); @@ -136,10 +206,7 @@ class AuthController extends Controller $this->throwValidationException( $request, $validator ); - // @codeCoverageIgnoreStart } - // @codeCoverageIgnoreEnd - $data = $request->all(); $data['password'] = bcrypt($data['password']); @@ -152,7 +219,8 @@ class AuthController extends Controller ); } - Auth::login($this->create($data)); + + Auth::login($this->create($request->all())); // get the email address if (Auth::user() instanceof User) { @@ -187,8 +255,8 @@ class AuthController extends Controller // @codeCoverageIgnoreStart abort(500, 'Not a user!'); - return redirect('/'); - // @codeCoverageIgnoreEnd + + return redirect($this->redirectPath()); } /** @@ -225,37 +293,4 @@ class AuthController extends Controller return false; } - /** - * Get a validator for an incoming registration request. - * - * @param array $data - * - * @return \Illuminate\Contracts\Validation\Validator - */ - public function validator(array $data) - { - return Validator::make( - $data, [ - 'email' => 'required|email|max:255|unique:users', - 'password' => 'required|confirmed|min:6', - ] - ); - } - - /** - * Create a new user instance after a valid registration. - * - * @param array $data - * - * @return User - */ - public function create(array $data) - { - return User::create( - [ - 'email' => $data['email'], - 'password' => $data['password'], - ] - ); - } } diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php old mode 100644 new mode 100755 index 72944e31f1..855f8895a9 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -1,4 +1,6 @@ -validate($request, ['email' => 'required|email']); @@ -70,16 +62,13 @@ class PasswordController extends Controller switch ($response) { case Password::RESET_LINK_SENT: - return redirect()->back()->with('status', trans($response)); + return $this->getSendResetLinkEmailSuccessResponse($response); case Password::INVALID_USER: case 'passwords.blocked': - return redirect()->back()->withErrors(['email' => trans($response)]); - + default: + return $this->getSendResetLinkEmailFailureResponse($response); } - abort(404); - - return ''; } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php old mode 100644 new mode 100755 index 55d0bc1765..7a0c715502 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -1,30 +1,27 @@ - [ + \FireflyIII\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \FireflyIII\Http\Middleware\VerifyCsrfToken::class, + ], + + 'api' => [ + 'throttle:60,1', + ], ]; /** * The application's route middleware. * + * These middleware may be assigned to groups or used individually. + * * @var array */ protected $routeMiddleware = [ - 'auth' => 'FireflyIII\Http\Middleware\Authenticate', - 'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth', - 'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated', - 'range' => 'FireflyIII\Http\Middleware\Range', - + 'auth' => \FireflyIII\Http\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'guest' => \FireflyIII\Http\Middleware\RedirectIfAuthenticated::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'range' => \FireflyIII\Http\Middleware\Range::class, ]; - } diff --git a/app/Http/routes.php b/app/Http/routes.php index 891e36424e..8e6aba0054 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -260,21 +260,18 @@ Route::bind( ); -/** - * Auth\AuthController - */ -Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']); +// auth routes, i think +Route::group( + ['middleware' => 'web'], function () { + Route::auth(); -Route::controllers( - [ - 'auth' => 'Auth\AuthController', - 'password' => 'Auth\PasswordController', - ] + Route::get('/home', 'HomeController@index'); +} ); Route::group( - ['middleware' => ['auth', 'range']], function () { + ['middleware' => ['auth', 'range', 'web']], function () { /** * Home Controller From 8c37ef3a95576ff83517d6f96218971c0028c9d0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:02:15 +0100 Subject: [PATCH 019/276] Updated various files after upgrade to laravel 5.2 --- app/Commands/Command.php | 12 ------- app/Console/Kernel.php | 22 ++++--------- app/Events/Event.php | 12 ++----- app/Exceptions/Handler.php | 62 ++++++++++++++++-------------------- app/User.php | 64 ++++++-------------------------------- 5 files changed, 45 insertions(+), 127 deletions(-) delete mode 100644 app/Commands/Command.php mode change 100644 => 100755 app/Console/Kernel.php mode change 100644 => 100755 app/Events/Event.php mode change 100644 => 100755 app/Exceptions/Handler.php mode change 100644 => 100755 app/User.php diff --git a/app/Commands/Command.php b/app/Commands/Command.php deleted file mode 100644 index 1212b4e554..0000000000 --- a/app/Commands/Command.php +++ /dev/null @@ -1,12 +0,0 @@ -renderHttpException($e); - } else { - return parent::render($request, $e); - } - } + protected $dontReport = [ + AuthorizationException::class, + HttpException::class, + ModelNotFoundException::class, + ValidationException::class, + ]; /** * Report or log an exception. * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. - * @SuppressWarnings(PHPMD.ShortVariable) - * - * @param \Exception $e * + * @param \Exception $e * @return void */ public function report(Exception $e) { - parent::report($e); + return parent::report($e); } + /** + * Render an exception into an HTTP response. + * + * @param \Illuminate\Http\Request $request + * @param \Exception $e + * @return \Illuminate\Http\Response + */ + public function render($request, Exception $e) + { + return parent::render($request, $e); + } } diff --git a/app/User.php b/app/User.php old mode 100644 new mode 100755 index d1924bbccc..606030315e --- a/app/User.php +++ b/app/User.php @@ -1,73 +1,27 @@ - Date: Fri, 8 Jan 2016 16:09:51 +0100 Subject: [PATCH 020/276] Updated various files after upgrade to laravel 5.2 --- composer.json | 131 ++-- composer.lock | 1833 +++++++++++++++++++++++++++++++++++++++---------- gulpfile.js | 6 +- package.json | 8 +- phpunit.xml | 14 +- server.php | 5 +- 6 files changed, 1540 insertions(+), 457 deletions(-) mode change 100644 => 100755 composer.json mode change 100644 => 100755 gulpfile.js mode change 100644 => 100755 package.json mode change 100644 => 100755 phpunit.xml mode change 100644 => 100755 server.php diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 index 47b7746e25..8543ca1420 --- a/composer.json +++ b/composer.json @@ -1,68 +1,71 @@ { - "name": "grumpydictator/firefly-iii", - "description": "Firefly III: a personal finances manager.", - "keywords": ["finance", "finances", "manager", "euro", "laravel", "money", "financials", "budgets", "transactions", "transfers", "management"], - "license": "MIT", - "homepage": "https://github.com/JC5/firefly-iii", - "type": "project", - "authors": [ - { - "name": "James Cole", - "email": "thegrumpydictator@gmail.com", - "homepage": "https://github.com/JC5", - "role": "Developer" - } - ], - "support": { - "email": "thegrumpydictator@gmail.com", - "issues": "https://github.com/JC5/firefly-iii/issues?state=open", - "source": "https://github.com/JC5/firefly-iii" + "name": "grumpydictator/firefly-iii", + "description": "Firefly III: a personal finances manager.", + "keywords": ["finance", "finances", "manager", "euro", "laravel", "money", "financials", "budgets", "transactions", "transfers", "management"], + "license": "MIT", + "homepage": "https://github.com/JC5/firefly-iii", + "type": "project", + "authors": [ + { + "name": "James Cole", + "email": "thegrumpydictator@gmail.com", + "homepage": "https://github.com/JC5", + "role": "Developer" + }], - }, - "require": { - "laravel/framework": "5.1.*", - "php": ">=5.6.4", - "davejamesmiller/laravel-breadcrumbs": "~3.0", - "watson/validating": "~1.0", - "doctrine/dbal": "~2.5", - "illuminate/html": "~5.0", - "league/commonmark": "~0.7", - "rcrowe/twigbridge": "~0.9", - "zizaco/entrust": "dev-laravel-5", - "league/csv": "^7.1" - }, - "require-dev": { - "barryvdh/laravel-debugbar": "@stable", - "barryvdh/laravel-ide-helper": "~2.0" - }, - "autoload": { - "classmap": [ - "database" - ], - "psr-4": { - "FireflyIII\\": "app/" - } - }, - "autoload-dev": { - "classmap": [ - "tests/TestCase.php" - ] - }, - "scripts": { - "post-install-cmd": [ - "php artisan clear-compiled", - "php artisan optimize" - ], - "post-update-cmd": [ - "php artisan clear-compiled", - "php artisan optimize" - ], - "post-create-project-cmd": [ - "php -r \"copy('.env.example', '.env');\"", - "php artisan key:generate" - ] - }, - "config": { - "preferred-install": "dist" + "require": { + "php": ">=5.6.11", + "laravel/framework": "5.2.*", + "davejamesmiller/laravel-breadcrumbs": "~3.0", + "watson/validating": "~2.0", + "doctrine/dbal": "~2.5", + "league/commonmark": "~0.7", + "rcrowe/twigbridge": "~0.9", + "zizaco/entrust": "dev-laravel-5", + "league/csv": "^7.1", + "laravelcollective/html": "^5.2" + }, + "require-dev": { + "fzaninotto/faker": "~1.4", + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "~4.0", + "symfony/css-selector": "2.8.*|3.0.*", + "symfony/dom-crawler": "2.8.*|3.0.*", + "barryvdh/laravel-debugbar": "@stable", + "barryvdh/laravel-ide-helper": "~2.0" + }, + "autoload": { + "classmap": [ + "database" + ], + "psr-4": { + "FireflyIII\\": "app/" } + }, + "autoload-dev": { + "classmap": [ + "tests/TestCase.php" + ] + }, + "scripts": { + "post-root-package-install": [ + "php -r \"copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "php artisan key:generate" + ], + "post-install-cmd": [ + "php artisan clear-compiled", + "php artisan optimize" + ], + "pre-update-cmd": [ + "php artisan clear-compiled" + ], + "post-update-cmd": [ + "php artisan optimize" + ] + }, + "config": { + "preferred-install": "dist" + } } diff --git a/composer.lock b/composer.lock index 943a47391f..a7e88bbdc1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "35ee47a928ca01f96176d94b1bdbd800", - "content-hash": "1d6d8db5b01d70aed4926680b5236331", + "hash": "1dbccadc167d6bc59012345c9873722e", + "content-hash": "ab9579d4e3b4d532ff42f82aa009f096", "packages": [ { "name": "classpreloader/classpreloader", @@ -61,62 +61,6 @@ ], "time": "2015-11-09 22:51:51" }, - { - "name": "danielstjules/stringy", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/danielstjules/Stringy.git", - "reference": "4749c205db47ee5b32e8d1adf6d9aff8db6caf3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/danielstjules/Stringy/zipball/4749c205db47ee5b32e8d1adf6d9aff8db6caf3b", - "reference": "4749c205db47ee5b32e8d1adf6d9aff8db6caf3b", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Stringy\\": "src/" - }, - "files": [ - "src/Create.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel St. Jules", - "email": "danielst.jules@gmail.com", - "homepage": "http://www.danielstjules.com" - } - ], - "description": "A string manipulation library with multibyte support", - "homepage": "https://github.com/danielstjules/Stringy", - "keywords": [ - "UTF", - "helpers", - "manipulation", - "methods", - "multibyte", - "string", - "utf-8", - "utility", - "utils" - ], - "time": "2015-07-23 00:54:12" - }, { "name": "davejamesmiller/laravel-breadcrumbs", "version": "3.0.0", @@ -668,52 +612,6 @@ ], "time": "2014-09-09 13:34:57" }, - { - "name": "illuminate/html", - "version": "v5.0.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/html.git", - "reference": "3d1009bb8e0f25720c914af5c1f4015dd373c9ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/html/zipball/3d1009bb8e0f25720c914af5c1f4015dd373c9ef", - "reference": "3d1009bb8e0f25720c914af5c1f4015dd373c9ef", - "shasum": "" - }, - "require": { - "illuminate/http": "~5.0", - "illuminate/session": "~5.0", - "illuminate/support": "~5.0", - "php": ">=5.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Html\\": "" - }, - "files": [ - "helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "time": "2015-01-01 16:31:18" - }, { "name": "jakub-onderka/php-console-color", "version": "0.1", @@ -861,45 +759,43 @@ }, { "name": "laravel/framework", - "version": "v5.1.28", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "3f0fd27939dfdafb1e50058423cd24e640894ba2" + "reference": "26cd65eaa4bcc0fb0be381cfb7cfdcda06a3c2b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/3f0fd27939dfdafb1e50058423cd24e640894ba2", - "reference": "3f0fd27939dfdafb1e50058423cd24e640894ba2", + "url": "https://api.github.com/repos/laravel/framework/zipball/26cd65eaa4bcc0fb0be381cfb7cfdcda06a3c2b4", + "reference": "26cd65eaa4bcc0fb0be381cfb7cfdcda06a3c2b4", "shasum": "" }, "require": { - "classpreloader/classpreloader": "~2.0|~3.0", - "danielstjules/stringy": "~1.8", + "classpreloader/classpreloader": "~3.0", "doctrine/inflector": "~1.0", "ext-mbstring": "*", "ext-openssl": "*", - "jeremeamia/superclosure": "~2.0", + "jeremeamia/superclosure": "~2.2", "league/flysystem": "~1.0", "monolog/monolog": "~1.11", "mtdowling/cron-expression": "~1.0", - "nesbot/carbon": "~1.19", + "nesbot/carbon": "~1.20", "paragonie/random_compat": "~1.1", "php": ">=5.5.9", "psy/psysh": "0.6.*", "swiftmailer/swiftmailer": "~5.1", - "symfony/console": "2.7.*", - "symfony/css-selector": "2.7.*", - "symfony/debug": "2.7.*", - "symfony/dom-crawler": "2.7.*", - "symfony/finder": "2.7.*", - "symfony/http-foundation": "2.7.*", - "symfony/http-kernel": "2.7.*", - "symfony/process": "2.7.*", - "symfony/routing": "2.7.*", - "symfony/translation": "2.7.*", - "symfony/var-dumper": "2.7.*", - "vlucas/phpdotenv": "~1.0" + "symfony/console": "2.8.*|3.0.*", + "symfony/debug": "2.8.*|3.0.*", + "symfony/finder": "2.8.*|3.0.*", + "symfony/http-foundation": "2.8.*|3.0.*", + "symfony/http-kernel": "2.8.*|3.0.*", + "symfony/polyfill-php56": "~1.0", + "symfony/process": "2.8.*|3.0.*", + "symfony/routing": "2.8.*|3.0.*", + "symfony/translation": "2.8.*|3.0.*", + "symfony/var-dumper": "2.8.*|3.0.*", + "vlucas/phpdotenv": "~2.2" }, "replace": { "illuminate/auth": "self.version", @@ -933,28 +829,30 @@ }, "require-dev": { "aws/aws-sdk-php": "~3.0", - "iron-io/iron_mq": "~2.0", "mockery/mockery": "~0.9.2", "pda/pheanstalk": "~3.0", - "phpunit/phpunit": "~4.0", - "predis/predis": "~1.0" + "phpunit/phpunit": "~4.1", + "predis/predis": "~1.0", + "symfony/css-selector": "2.8.*|3.0.*", + "symfony/dom-crawler": "2.8.*|3.0.*" }, "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.4).", "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 (~5.3|~6.0).", - "iron-io/iron_mq": "Required to use the iron queue driver (~2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~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 (~2.0)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", + "symfony/css-selector": "Required to use some of the crawler integration testing tools (2.8.*|3.0.*).", + "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (2.8.*|3.0.*)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "5.2-dev" } }, "autoload": { @@ -985,7 +883,61 @@ "framework", "laravel" ], - "time": "2015-12-31 17:41:30" + "time": "2016-01-07 13:54:34" + }, + { + "name": "laravelcollective/html", + "version": "v5.2", + "source": { + "type": "git", + "url": "https://github.com/LaravelCollective/html.git", + "reference": "5a8f1380a0d5ef21cdef37e775d86566307f8358" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LaravelCollective/html/zipball/5a8f1380a0d5ef21cdef37e775d86566307f8358", + "reference": "5a8f1380a0d5ef21cdef37e775d86566307f8358", + "shasum": "" + }, + "require": { + "illuminate/http": "5.2.*", + "illuminate/routing": "5.2.*", + "illuminate/session": "5.2.*", + "illuminate/support": "5.2.*", + "illuminate/view": "5.2.*", + "php": ">=5.5.9" + }, + "require-dev": { + "illuminate/database": "5.2.*", + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Collective\\Html\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylorotwell@gmail.com" + }, + { + "name": "Adam Engebretson", + "email": "adam@laravelcollective.com" + } + ], + "description": "HTML and Form Builders for the Laravel Framework", + "homepage": "http://laravelcollective.com", + "time": "2015-12-23 07:46:46" }, { "name": "league/commonmark", @@ -1415,16 +1367,16 @@ }, { "name": "paragonie/random_compat", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "d762ee5b099a29044603cd4649851e81aa66cb47" + "reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/d762ee5b099a29044603cd4649851e81aa66cb47", - "reference": "d762ee5b099a29044603cd4649851e81aa66cb47", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/dd8998b7c846f6909f4e7a5f67fabebfc412a4f7", + "reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7", "shasum": "" }, "require": { @@ -1459,7 +1411,7 @@ "pseudorandom", "random" ], - "time": "2015-12-10 14:48:13" + "time": "2016-01-06 13:31:20" }, { "name": "psr/log", @@ -1690,25 +1642,26 @@ }, { "name": "symfony/console", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "4e35a78f932a4c07bd349efea647ac741c1419b6" + "reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/4e35a78f932a4c07bd349efea647ac741c1419b6", - "reference": "4e35a78f932a4c07bd349efea647ac741c1419b6", + "url": "https://api.github.com/repos/symfony/console/zipball/ebcdc507829df915f4ca23067bd59ee4ef61f6c3", + "reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1", - "symfony/process": "~2.1" + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" }, "suggest": { "psr/log": "For using the console logger", @@ -1718,7 +1671,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1745,90 +1698,37 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-12-23 11:17:38" - }, - { - "name": "symfony/css-selector", - "version": "v2.7.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "35bebec48d3d08e3138257419e3ca84070152012" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/35bebec48d3d08e3138257419e3ca84070152012", - "reference": "35bebec48d3d08e3138257419e3ca84070152012", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2015-12-05 17:37:09" + "time": "2015-12-22 10:39:06" }, { "name": "symfony/debug", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "08589346bd2ec9a8eb3d935e3b1fedba9bb6463f" + "reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/08589346bd2ec9a8eb3d935e3b1fedba9bb6463f", - "reference": "08589346bd2ec9a8eb3d935e3b1fedba9bb6463f", + "url": "https://api.github.com/repos/symfony/debug/zipball/73612266ac709769effdbfc0762e5b07cfd2ac2a", + "reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a", "shasum": "" }, "require": { - "php": ">=5.3.9", + "php": ">=5.5.9", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/class-loader": "~2.2", - "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2" + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1855,86 +1755,31 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2015-12-26 14:05:15" - }, - { - "name": "symfony/dom-crawler", - "version": "v2.7.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "b33593cbfe1d81b50d48353f338aca76a08658d8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b33593cbfe1d81b50d48353f338aca76a08658d8", - "reference": "b33593cbfe1d81b50d48353f338aca76a08658d8", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "symfony/css-selector": "~2.3" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DomCrawler Component", - "homepage": "https://symfony.com", - "time": "2015-11-02 20:20:53" + "time": "2015-12-26 13:39:53" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.1", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc" + "reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc", - "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d36355e026905fa5229e1ed7b4e9eda2e67adfcf", + "reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1943,7 +1788,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1970,29 +1815,29 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2015-10-30 23:35:59" }, { "name": "symfony/finder", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "937edcbac3f2dd3187c56cf90368867d55dee991" + "reference": "8617895eb798b6bdb338321ce19453dc113e5675" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/937edcbac3f2dd3187c56cf90368867d55dee991", - "reference": "937edcbac3f2dd3187c56cf90368867d55dee991", + "url": "https://api.github.com/repos/symfony/finder/zipball/8617895eb798b6bdb338321ce19453dc113e5675", + "reference": "8617895eb798b6bdb338321ce19453dc113e5675", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2019,41 +1864,38 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2015-12-05 11:06:38" + "time": "2015-12-05 11:13:14" }, { "name": "symfony/http-foundation", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cf11faac7df5384bb14774ad7266add227e10ec1" + "reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cf11faac7df5384bb14774ad7266add227e10ec1", - "reference": "cf11faac7df5384bb14774ad7266add227e10ec1", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5", + "reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "require-dev": { - "symfony/expression-language": "~2.4" + "symfony/expression-language": "~2.8|~3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, - "classmap": [ - "Resources/stubs" - ], "exclude-from-classmap": [ "/Tests/" ] @@ -2074,48 +1916,48 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2015-12-18 15:35:58" + "time": "2015-12-18 15:43:53" }, { "name": "symfony/http-kernel", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "2dea13800e1a48710cf23a2c60c804c88e72ed57" + "reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2dea13800e1a48710cf23a2c60c804c88e72ed57", - "reference": "2dea13800e1a48710cf23a2c60c804c88e72ed57", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f7933e9f19e26e7baba7ec04735b466fedd3a6db", + "reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db", "shasum": "" }, "require": { - "php": ">=5.3.9", + "php": ">=5.5.9", "psr/log": "~1.0", - "symfony/debug": "~2.6,>=2.6.2", - "symfony/event-dispatcher": "~2.6,>=2.6.7", - "symfony/http-foundation": "~2.5,>=2.5.4" + "symfony/debug": "~2.8|~3.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0" }, "conflict": { - "symfony/config": "<2.7" + "symfony/config": "<2.8" }, "require-dev": { - "symfony/browser-kit": "~2.3", - "symfony/class-loader": "~2.1", - "symfony/config": "~2.7", - "symfony/console": "~2.3", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.2", - "symfony/dom-crawler": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.4", - "symfony/finder": "~2.0,>=2.0.5", - "symfony/process": "~2.0,>=2.0.5", - "symfony/routing": "~2.2", - "symfony/stopwatch": "~2.3", - "symfony/templating": "~2.2", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/var-dumper": "~2.6" + "symfony/browser-kit": "~2.8|~3.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/routing": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/var-dumper": "~2.8|~3.0" }, "suggest": { "symfony/browser-kit": "", @@ -2129,7 +1971,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2156,7 +1998,66 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2015-12-26 15:01:55" + "time": "2015-12-26 16:46:13" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/49ff736bd5d41f45240cec77b44967d76e0c3d25", + "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-20 09:19:13" }, { "name": "symfony/polyfill-php56", @@ -2268,25 +2169,25 @@ }, { "name": "symfony/process", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "a3fb8f4c4afc4f1b285de5df07e568602934f525" + "reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/a3fb8f4c4afc4f1b285de5df07e568602934f525", - "reference": "a3fb8f4c4afc4f1b285de5df07e568602934f525", + "url": "https://api.github.com/repos/symfony/process/zipball/f4794f1d00f0746621be3020ffbd8c5e0b217ee3", + "reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2313,47 +2214,48 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2015-12-23 11:03:39" + "time": "2015-12-23 11:04:02" }, { "name": "symfony/routing", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "2c100bd94be50e2a1fce7fe1ac534e28776c24ff" + "reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/2c100bd94be50e2a1fce7fe1ac534e28776c24ff", - "reference": "2c100bd94be50e2a1fce7fe1ac534e28776c24ff", + "url": "https://api.github.com/repos/symfony/routing/zipball/3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59", + "reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "conflict": { - "symfony/config": "<2.7" + "symfony/config": "<2.8" }, "require-dev": { "doctrine/annotations": "~1.0", "doctrine/common": "~2.2", "psr/log": "~1.0", - "symfony/config": "~2.7", - "symfony/expression-language": "~2.4", - "symfony/http-foundation": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/config": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" }, "suggest": { "doctrine/annotations": "For using the annotation loader", "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", "symfony/expression-language": "For using expression matching", "symfony/yaml": "For using the YAML loader" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2386,33 +2288,34 @@ "uri", "url" ], - "time": "2015-12-23 06:54:35" + "time": "2015-12-23 08:00:11" }, { "name": "symfony/translation", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "e7e95debf0465f7886d2994cd808f9382adb423c" + "reference": "dff0867826a7068d673801b7522f8e2634016ef9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e7e95debf0465f7886d2994cd808f9382adb423c", - "reference": "e7e95debf0465f7886d2994cd808f9382adb423c", + "url": "https://api.github.com/repos/symfony/translation/zipball/dff0867826a7068d673801b7522f8e2634016ef9", + "reference": "dff0867826a7068d673801b7522f8e2634016ef9", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/config": "<2.7" + "symfony/config": "<2.8" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.7", - "symfony/intl": "~2.4", - "symfony/yaml": "~2.2" + "symfony/config": "~2.8|~3.0", + "symfony/intl": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" }, "suggest": { "psr/log": "To use logging capability in translator", @@ -2422,7 +2325,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2449,24 +2352,28 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2015-12-05 17:37:09" + "time": "2015-12-05 17:45:07" }, { "name": "symfony/var-dumper", - "version": "v2.7.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "ec3233d755578c56612c13d81d4ef141f8f94e9e" + "reference": "87db8700deb12ba2b65e858f656a1f885530bcb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ec3233d755578c56612c13d81d4ef141f8f94e9e", - "reference": "ec3233d755578c56612c13d81d4ef141f8f94e9e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/87db8700deb12ba2b65e858f656a1f885530bcb0", + "reference": "87db8700deb12ba2b65e858f656a1f885530bcb0", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "twig/twig": "~1.20|~2.0" }, "suggest": { "ext-symfony_debug": "" @@ -2474,7 +2381,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2508,7 +2415,7 @@ "debug", "dump" ], - "time": "2015-12-05 10:02:55" + "time": "2015-12-05 11:13:14" }, { "name": "twig/twig", @@ -2573,28 +2480,33 @@ }, { "name": "vlucas/phpdotenv", - "version": "v1.1.1", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "0cac554ce06277e33ddf9f0b7ade4b8bbf2af3fa" + "reference": "9caf304153dc2288e4970caec6f1f3b3bc205412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/0cac554ce06277e33ddf9f0b7ade4b8bbf2af3fa", - "reference": "0cac554ce06277e33ddf9f0b7ade4b8bbf2af3fa", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/9caf304153dc2288e4970caec6f1f3b3bc205412", + "reference": "9caf304153dc2288e4970caec6f1f3b3bc205412", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^4.8|^5.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, "autoload": { - "psr-0": { - "Dotenv": "src/" + "psr-4": { + "Dotenv\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2615,28 +2527,28 @@ "env", "environment" ], - "time": "2015-05-30 15:59:26" + "time": "2015-12-29 15:10:30" }, { "name": "watson/validating", - "version": "1.0.9", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/dwightwatson/validating.git", - "reference": "0b225ddc3187745ff9efcbcd0d9b4b712df190ba" + "reference": "508c130ea82bc83c65071ffcf1680cdfa16412d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dwightwatson/validating/zipball/0b225ddc3187745ff9efcbcd0d9b4b712df190ba", - "reference": "0b225ddc3187745ff9efcbcd0d9b4b712df190ba", + "url": "https://api.github.com/repos/dwightwatson/validating/zipball/508c130ea82bc83c65071ffcf1680cdfa16412d6", + "reference": "508c130ea82bc83c65071ffcf1680cdfa16412d6", "shasum": "" }, "require": { - "illuminate/contracts": "5.0.*|5.1.*", - "illuminate/database": "5.0.*|5.1.*", - "illuminate/events": "5.0.*|5.1.*", - "illuminate/support": "5.0.*|5.1.*", - "illuminate/validation": "5.0.*|5.1.*", + "illuminate/contracts": "~5.0", + "illuminate/database": "~5.0 || >=5.1.27", + "illuminate/events": "~5.0", + "illuminate/support": "~5.0", + "illuminate/validation": "~5.0", "php": ">=5.4.0" }, "require-dev": { @@ -2670,7 +2582,7 @@ "laravel", "validation" ], - "time": "2015-12-16 23:15:51" + "time": "2015-12-25 23:19:17" }, { "name": "zizaco/entrust", @@ -2858,6 +2770,157 @@ ], "time": "2015-12-21 19:48:06" }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "fzaninotto/faker", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "d0190b156bcca848d401fb80f31f504f37141c8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d0190b156bcca848d401fb80f31f504f37141c8d", + "reference": "d0190b156bcca848d401fb80f31f504f37141c8d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "suggest": { + "ext-intl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2015-05-29 06:29:14" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "1.3.3", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "hamcrest" + ], + "files": [ + "hamcrest/Hamcrest.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2015-05-11 14:41:42" + }, { "name": "maximebf/debugbar", "version": "v1.11.0", @@ -2919,6 +2982,71 @@ ], "time": "2015-12-10 09:50:24" }, + { + "name": "mockery/mockery", + "version": "0.9.4", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "~1.1", + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2015-04-02 19:54:00" + }, { "name": "phpdocumentor/reflection-docblock", "version": "2.0.4", @@ -2968,6 +3096,805 @@ ], "time": "2015-02-03 12:10:50" }, + { + "name": "phpspec/prophecy", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", + "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2015-08-13 10:07:40" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06 15:47:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2015-06-21 13:08:43" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2015-06-21 08:01:12" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2015-09-15 10:49:45" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.21", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "ea76b17bced0500a28098626b84eda12dbcf119c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c", + "reference": "ea76b17bced0500a28098626b84eda12dbcf119c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": ">=1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2015-12-12 07:45:58" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02 06:51:40" + }, + { + "name": "sebastian/comparator", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-07-26 15:48:44" + }, + { + "name": "sebastian/diff", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08 07:14:41" + }, + { + "name": "sebastian/environment", + "version": "1.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6e7133793a8e5a5714a551a8324337374be209df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", + "reference": "6e7133793a8e5a5714a551a8324337374be209df", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2015-12-02 08:37:27" + }, + { + "name": "sebastian/exporter", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", + "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2015-06-21 07:55:53" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-11-11 19:50:13" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21 13:59:46" + }, { "name": "symfony/class-loader", "version": "v2.8.1", @@ -3019,6 +3946,164 @@ "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", "time": "2015-12-05 17:37:59" + }, + { + "name": "symfony/css-selector", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3", + "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2015-12-05 17:45:07" + }, + { + "name": "symfony/dom-crawler", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d", + "reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "https://symfony.com", + "time": "2015-12-26 13:42:31" + }, + { + "name": "symfony/yaml", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691", + "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2015-12-26 13:39:53" } ], "aliases": [], @@ -3030,7 +4115,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.6.4" + "php": ">=5.6.11" }, "platform-dev": [] } diff --git a/gulpfile.js b/gulpfile.js old mode 100644 new mode 100755 index 14dd43eabf..dc6f1ebb4e --- a/gulpfile.js +++ b/gulpfile.js @@ -1,4 +1,3 @@ -/* globals require */ var elixir = require('laravel-elixir'); /* @@ -7,12 +6,11 @@ var elixir = require('laravel-elixir'); |-------------------------------------------------------------------------- | | Elixir provides a clean, fluent API for defining some basic Gulp tasks - | for your Laravel application. By default, we are compiling the Less + | for your Laravel application. By default, we are compiling the Sass | file for our application, as well as publishing vendor resources. | */ elixir(function(mix) { - "use strict"; - mix.less('app.less'); + mix.sass('app.scss'); }); diff --git a/package.json b/package.json old mode 100644 new mode 100755 index f45052ae2e..460ee907b5 --- a/package.json +++ b/package.json @@ -1,6 +1,10 @@ { + "private": true, "devDependencies": { - "gulp": "^3.8.8", - "laravel-elixir": "*" + "gulp": "^3.8.8" + }, + "dependencies": { + "laravel-elixir": "^4.0.0", + "bootstrap-sass": "^3.0.0" } } diff --git a/phpunit.xml b/phpunit.xml old mode 100644 new mode 100755 index 6b3ab70282..cc0841c1d3 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,25 +7,17 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" - syntaxCheck="false"> + stopOnFailure="false"> - ./tests + ./tests/ - ./app + app/ - - - diff --git a/server.php b/server.php old mode 100644 new mode 100755 index c6499cf326..f65c7c444f --- a/server.php +++ b/server.php @@ -1,4 +1,5 @@ Date: Fri, 8 Jan 2016 16:29:14 +0100 Subject: [PATCH 021/276] Debug class does not belong in production. --- config/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/app.php b/config/app.php index 58ba0b91ce..aa8b880314 100755 --- a/config/app.php +++ b/config/app.php @@ -160,7 +160,7 @@ return [ FireflyIII\Providers\FireflyServiceProvider::class, // own stuff: - Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, +// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, 'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'TwigBridge\ServiceProvider', From e8e8163fa7db6e1cb0055fe7ba37891c1c998d51 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:31:27 +0100 Subject: [PATCH 022/276] Allow registering by default. --- config/auth.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/auth.php b/config/auth.php index 46cc20e0eb..f42b57640d 100755 --- a/config/auth.php +++ b/config/auth.php @@ -2,6 +2,8 @@ return [ + 'allow_register' => true, + /* |-------------------------------------------------------------------------- | Authentication Defaults From 5847f534c33629aa84a550b23c2e618449e47007 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:32:31 +0100 Subject: [PATCH 023/276] Fix register route. --- resources/views/auth/login.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/auth/login.twig b/resources/views/auth/login.twig index 4b143cf6af..1257985a91 100644 --- a/resources/views/auth/login.twig +++ b/resources/views/auth/login.twig @@ -42,7 +42,7 @@
{% if Config.get('auth.allow_register') %} - Register a new account
+ Register a new account
{% endif %} I forgot my password
From b1b2fda155ebd1d75f91e6c60be9778bd40abea8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:32:51 +0100 Subject: [PATCH 024/276] No 5.5 test. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 820282fa3a..383b324dc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false php: - - 5.5 - 5.6 install: From 95a456860a44ef460e0033e9a53cb11e6394a721 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:33:27 +0100 Subject: [PATCH 025/276] Fix another URL. --- resources/views/auth/register.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/auth/register.twig b/resources/views/auth/register.twig index 03970a73b9..c2c82b142c 100644 --- a/resources/views/auth/register.twig +++ b/resources/views/auth/register.twig @@ -22,7 +22,7 @@ work for one (1) month.

{% endif %} -
+
From ea6896816dd5e538b84fde3f4b6c45e454d95ef3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:34:52 +0100 Subject: [PATCH 026/276] Fix domain check. --- app/Http/Controllers/Auth/AuthController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 26510cf6a3..3901189a74 100755 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -264,7 +264,7 @@ class AuthController extends Controller */ protected function getBlockedDomains() { - $set = Config::get('mail.blocked_domains'); + $set = explode(',',env('BLOCKED_DOMAINS','')); $domains = []; foreach ($set as $entry) { $domain = trim($entry); From 780abecd536277485acde9e0d71a86ec0e9d51a7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:35:44 +0100 Subject: [PATCH 027/276] Worked the last time.. --- app/Http/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index b4fcc1e54c..cafbc40e61 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -26,7 +26,7 @@ class Kernel extends HttpKernel protected $middlewareGroups = [ 'web' => [ - \FireflyIII\Http\Middleware\EncryptCookies::class, +// \FireflyIII\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, From 78c8243184619f51d876adbbfc7e08d0586a0557 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:36:41 +0100 Subject: [PATCH 028/276] Not the problem. --- app/Http/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index cafbc40e61..b4fcc1e54c 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -26,7 +26,7 @@ class Kernel extends HttpKernel protected $middlewareGroups = [ 'web' => [ -// \FireflyIII\Http\Middleware\EncryptCookies::class, + \FireflyIII\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, From c0fad106f0b4c737ec7ed5eb88508b22ea2fbb83 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 16:38:20 +0100 Subject: [PATCH 029/276] Temp fix for redirect loop. --- app/Http/Controllers/Auth/AuthController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 3901189a74..14f0855ac1 100755 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -93,7 +93,7 @@ class AuthController extends Controller $credentials = $this->getCredentials($request); $credentials['blocked'] = 0; // most not be blocked. - if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) { + if (Auth::guard($this->getGuard())->attempt($credentials, true)) { return $this->handleUserWasAuthenticated($request, $throttles); } From fd9a7080eacbd54dec07f4962f725ab9a07d626d Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 18:29:47 +0100 Subject: [PATCH 030/276] Move authentication around. --- app/Http/Controllers/AccountController.php | 1 + app/Http/Controllers/AttachmentController.php | 1 + app/Http/Controllers/Auth/AuthController.php | 4 +++- app/Http/Controllers/BillController.php | 1 + app/Http/Controllers/BudgetController.php | 1 + app/Http/Controllers/CategoryController.php | 1 + app/Http/Controllers/Chart/AccountController.php | 1 + app/Http/Controllers/Chart/BillController.php | 1 + app/Http/Controllers/Chart/BudgetController.php | 1 + app/Http/Controllers/Chart/CategoryController.php | 1 + app/Http/Controllers/Chart/PiggyBankController.php | 1 + app/Http/Controllers/Chart/ReportController.php | 1 + app/Http/Controllers/CsvController.php | 1 + app/Http/Controllers/CurrencyController.php | 1 + app/Http/Controllers/HelpController.php | 4 ++++ app/Http/Controllers/HomeController.php | 6 ++++++ app/Http/Controllers/JsonController.php | 4 ++++ app/Http/Controllers/NewUserController.php | 4 ++++ app/Http/Controllers/PiggyBankController.php | 1 + app/Http/Controllers/PreferencesController.php | 1 + app/Http/Controllers/ProfileController.php | 4 ++++ app/Http/Controllers/ReportController.php | 2 ++ app/Http/Controllers/SearchController.php | 5 +++++ app/Http/Controllers/TagController.php | 1 + app/Http/Controllers/TransactionController.php | 1 + app/Http/routes.php | 4 ++-- 26 files changed, 51 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e018002bfc..4b043280fe 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -26,6 +26,7 @@ class AccountController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('mainTitleIcon', 'fa-credit-card'); View::share('title', trans('firefly.accounts')); diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 5a750ac786..41602ea21a 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -29,6 +29,7 @@ class AttachmentController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('mainTitleIcon', 'fa-paperclip'); View::share('title', trans('firefly.attachments')); diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 14f0855ac1..2ff6fde373 100755 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -22,6 +22,8 @@ use Validator; class AuthController extends Controller { + protected $guard = 'session'; + /* |-------------------------------------------------------------------------- | Registration & Login Controller @@ -93,7 +95,7 @@ class AuthController extends Controller $credentials = $this->getCredentials($request); $credentials['blocked'] = 0; // most not be blocked. - if (Auth::guard($this->getGuard())->attempt($credentials, true)) { + if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) { return $this->handleUserWasAuthenticated($request, $throttles); } diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index ca59f0d256..ed706b9315 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -24,6 +24,7 @@ class BillController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.bills')); View::share('mainTitleIcon', 'fa-calendar-o'); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 144cd44693..badaf90906 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -30,6 +30,7 @@ class BudgetController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.budgets')); View::share('mainTitleIcon', 'fa-tasks'); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index d564789c21..12b8272c75 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -29,6 +29,7 @@ class CategoryController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.categories')); View::share('mainTitleIcon', 'fa-bar-chart'); diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index dc6fe0cf13..0447f905b1 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -28,6 +28,7 @@ class AccountController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGenerator'); diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php index 5f05b25c5c..340cf2d025 100644 --- a/app/Http/Controllers/Chart/BillController.php +++ b/app/Http/Controllers/Chart/BillController.php @@ -27,6 +27,7 @@ class BillController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGenerator'); diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 39d3c4dffa..dffd7dca38 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -31,6 +31,7 @@ class BudgetController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator'); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 2314d61c74..362d13e701 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -31,6 +31,7 @@ class CategoryController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGenerator'); diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index a6972d2a75..2c811f79d0 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -26,6 +26,7 @@ class PiggyBankController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator'); diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 545fe46d7a..74ac96f6b9 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -26,6 +26,7 @@ class ReportController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGenerator'); diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index bde7d5b7e0..a78df75634 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -34,6 +34,7 @@ class CsvController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.csv')); View::share('mainTitleIcon', 'fa-file-text-o'); diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index d2bdbf7e06..3213934435 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -25,6 +25,7 @@ class CurrencyController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.currencies')); View::share('mainTitleIcon', 'fa-usd'); diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index 7220742700..adb1aa7245 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -11,6 +11,10 @@ use Response; */ class HelpController extends Controller { + public function __construct() + { + $this->middleware('auth'); + } /** * @param HelpInterface $help diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index a838ba8736..7f88eb38b3 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -6,6 +6,7 @@ use Config; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use Input; +use Log; use Preferences; use Session; use Steam; @@ -17,6 +18,10 @@ use Steam; */ class HomeController extends Controller { + public function __construct() + { + $this->middleware('auth'); + } public function dateRange() { @@ -68,6 +73,7 @@ class HomeController extends Controller */ public function index(ARI $repository) { + Log::debug('You are at index.'); $types = Config::get('firefly.accountTypesByIdentifier.asset'); $count = $repository->countAccounts($types); bcscale(2); diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index bc1b25539b..802173a18a 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -20,6 +20,10 @@ use Session; */ class JsonController extends Controller { + public function __construct() + { + $this->middleware('auth'); + } /** * @return \Illuminate\Http\JsonResponse diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 440c5c74d0..637a2ecb14 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -17,6 +17,10 @@ use View; */ class NewUserController extends Controller { + public function __construct() + { + $this->middleware('auth'); + } /** diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 92ceb4cb88..799e2f3c3a 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -31,6 +31,7 @@ class PiggyBankController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.piggyBanks')); View::share('mainTitleIcon', 'fa-sort-amount-asc'); diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 9cc7d3e861..fd0f4f2a77 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -20,6 +20,7 @@ class PreferencesController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.preferences')); View::share('mainTitleIcon', 'fa-gear'); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 71e3ffc473..133cc415a1 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -15,6 +15,10 @@ use Session; */ class ProfileController extends Controller { + public function __construct() + { + $this->middleware('auth'); + } /** * @return \Illuminate\View\View diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 59f4f112b4..b48566c11c 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -16,6 +16,7 @@ use View; class ReportController extends Controller { + /** @var ReportHelperInterface */ protected $helper; @@ -26,6 +27,7 @@ class ReportController extends Controller */ public function __construct(ReportHelperInterface $helper) { + $this->middleware('auth'); parent::__construct(); $this->helper = $helper; diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index ad3eb16b62..a9d2b30d55 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -10,6 +10,11 @@ use Input; */ class SearchController extends Controller { + public function __construct() + { + $this->middleware('auth'); + } + /** * Results always come in the form of an array [results, count, fullCount] * diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index bf0baa0805..4402d44ec2 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -39,6 +39,7 @@ class TagController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', 'Tags'); View::share('mainTitleIcon', 'fa-tags'); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 54e9b5fc2b..b8f1d067a2 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -37,6 +37,7 @@ class TransactionController extends Controller */ public function __construct() { + $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.transactions')); View::share('mainTitleIcon', 'fa-repeat'); diff --git a/app/Http/routes.php b/app/Http/routes.php index 8e6aba0054..83c4ab6249 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -265,13 +265,13 @@ Route::group( ['middleware' => 'web'], function () { Route::auth(); - Route::get('/home', 'HomeController@index'); + //Route::get('/home', 'HomeController@index'); } ); Route::group( - ['middleware' => ['auth', 'range', 'web']], function () { + ['middleware' => ['range', 'web']], function () { /** * Home Controller From 90e66cbd94f68cd43cbb427c8a114e3425a78aa0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 18:51:10 +0100 Subject: [PATCH 031/276] Remove session guard. --- app/Http/Controllers/Auth/AuthController.php | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 2ff6fde373..29d0374bf3 100755 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -3,7 +3,6 @@ namespace FireflyIII\Http\Controllers\Auth; use Auth; -use Config; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Role; use FireflyIII\User; @@ -16,25 +15,11 @@ use Log; use Mail; use Request as Rq; use Session; -use Twig; use Validator; class AuthController extends Controller { - protected $guard = 'session'; - - /* - |-------------------------------------------------------------------------- - | Registration & Login Controller - |-------------------------------------------------------------------------- - | - | This controller handles the registration of new users, as well as the - | authentication of existing users. By default, this controller uses - | a simple trait to add these behaviors. Why don't you explore it? - | - */ - use AuthenticatesAndRegistersUsers, ThrottlesLogins; /** @@ -44,7 +29,6 @@ class AuthController extends Controller */ protected $redirectTo = '/home'; - /** * Create a new authentication controller instance. * @@ -266,7 +250,7 @@ class AuthController extends Controller */ protected function getBlockedDomains() { - $set = explode(',',env('BLOCKED_DOMAINS','')); + $set = explode(',', env('BLOCKED_DOMAINS', '')); $domains = []; foreach ($set as $entry) { $domain = trim($entry); @@ -294,5 +278,4 @@ class AuthController extends Controller return false; } - } From 8b2d7fc32f191e31c7e007382ba05de1af1f051b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 19:13:51 +0100 Subject: [PATCH 032/276] Need to call parent constructor. --- app/Http/Controllers/HomeController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 7f88eb38b3..a0790dd9ba 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -21,6 +21,7 @@ class HomeController extends Controller public function __construct() { $this->middleware('auth'); + parent::__construct(); } public function dateRange() From b4c9a7698ef5b950f9d5361e67ef11cbde6fb938 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 20:40:48 +0100 Subject: [PATCH 033/276] Also call parent constructor. --- app/Http/Controllers/HelpController.php | 1 + app/Http/Controllers/JsonController.php | 1 + app/Http/Controllers/NewUserController.php | 1 + app/Http/Controllers/ProfileController.php | 1 + app/Http/Controllers/SearchController.php | 1 + 5 files changed, 5 insertions(+) diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index adb1aa7245..9c185c8dca 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -14,6 +14,7 @@ class HelpController extends Controller public function __construct() { $this->middleware('auth'); + parent::__construct(); } /** diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 802173a18a..28dc0df1f3 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -23,6 +23,7 @@ class JsonController extends Controller public function __construct() { $this->middleware('auth'); + parent::__construct(); } /** diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 637a2ecb14..37d905d42a 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -20,6 +20,7 @@ class NewUserController extends Controller public function __construct() { $this->middleware('auth'); + parent::__construct(); } diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 133cc415a1..fc1dce3869 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -18,6 +18,7 @@ class ProfileController extends Controller public function __construct() { $this->middleware('auth'); + parent::__construct(); } /** diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index a9d2b30d55..9f5dd863e0 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -13,6 +13,7 @@ class SearchController extends Controller public function __construct() { $this->middleware('auth'); + parent::__construct(); } /** From a3a1bc30b1297c53b1c43a78c80c7e4de65e6c82 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 20:43:46 +0100 Subject: [PATCH 034/276] Convert to number. --- app/Generator/Chart/Account/ChartJsAccountChartGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php index 20f59a922d..4e446f617b 100644 --- a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php +++ b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php @@ -114,10 +114,10 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator ]; $current = clone $start; $range = Steam::balanceInRange($account, $start, clone $end); - $previous = array_values($range)[0]; + $previous = round(array_values($range)[0], 2); while ($current <= $end) { $format = $current->format('Y-m-d'); - $balance = isset($range[$format]) ? $range[$format] : $previous; + $balance = isset($range[$format]) ? round($range[$format], 2) : $previous; $set['data'][] = $balance; $previous = $balance; From 84ce9bc94ba555228b8c13e6cef93f8ba75972f3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 20:47:35 +0100 Subject: [PATCH 035/276] Move locale code. --- app/Http/Controllers/Controller.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 7a0c715502..ea8b4c3a99 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -2,6 +2,7 @@ namespace FireflyIII\Http\Controllers; +use App; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; @@ -36,6 +37,14 @@ class Controller extends BaseController $this->monthFormat = trans('config.month'); $this->monthAndDayFormat = trans('config.month_and_day'); + App::setLocale($lang); + Carbon::setLocale(substr($lang, 0, 2)); + $locale = explode(',', trans('config.locale')); + $locale = array_map('trim', $locale); + + setlocale(LC_TIME, $locale); + setlocale(LC_MONETARY, $locale); + View::share('monthFormat', $this->monthFormat); View::share('monthAndDayFormat', $this->monthAndDayFormat); View::share('language', $lang); From e622774775be8ad6282650c7488c4fdec2780d49 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 8 Jan 2016 20:48:34 +0100 Subject: [PATCH 036/276] Move to better spot --- app/Http/Middleware/Authenticate.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index e7424954b4..aea4c9bbf0 100755 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -30,17 +30,6 @@ class Authenticate } } - // if logged in, set user language: - $pref = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US')); - App::setLocale($pref->data); - Carbon::setLocale(substr($pref->data, 0, 2)); - $locale = explode(',', trans('config.locale')); - $locale = array_map('trim', $locale); - - setlocale(LC_TIME, $locale); - setlocale(LC_MONETARY, $locale); - - return $next($request); } } From 9b24e6d448abb0aacd6d1de981352136a0ac930d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 07:32:23 +0100 Subject: [PATCH 037/276] Remove explanation text. --- config/auth.php | 84 ++++++------------------------------------------- 1 file changed, 9 insertions(+), 75 deletions(-) diff --git a/config/auth.php b/config/auth.php index f42b57640d..e0ad255263 100755 --- a/config/auth.php +++ b/config/auth.php @@ -3,73 +3,26 @@ return [ 'allow_register' => true, - - /* - |-------------------------------------------------------------------------- - | Authentication Defaults - |-------------------------------------------------------------------------- - | - | This option controls the default authentication "guard" and password - | reset options for your application. You may change these defaults - | as required, but they're a perfect start for most applications. - | - */ - - 'defaults' => [ - 'guard' => 'web', + 'defaults' => [ + 'guard' => 'web', 'passwords' => 'users', ], - - /* - |-------------------------------------------------------------------------- - | Authentication Guards - |-------------------------------------------------------------------------- - | - | Next, you may define every authentication guard for your application. - | Of course, a great default configuration has been defined for you - | here which uses session storage and the Eloquent user provider. - | - | All authentication drivers have a user provider. This defines how the - | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. - | - | Supported: "session", "token" - | - */ - - 'guards' => [ + 'guards' => [ 'web' => [ - 'driver' => 'session', + 'driver' => 'session', 'provider' => 'users', ], 'api' => [ - 'driver' => 'token', + 'driver' => 'token', 'provider' => 'users', ], ], - /* - |-------------------------------------------------------------------------- - | User Providers - |-------------------------------------------------------------------------- - | - | All authentication drivers have a user provider. This defines how the - | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. - | - | If you have multiple user tables or models you may configure multiple - | sources which represent each model / table. These sources may then - | be assigned to any extra authentication guards you have defined. - | - | Supported: "database", "eloquent" - | - */ - 'providers' => [ 'users' => [ 'driver' => 'eloquent', - 'model' => FireflyIII\User::class, + 'model' => FireflyIII\User::class, ], // 'users' => [ @@ -78,31 +31,12 @@ return [ // ], ], - /* - |-------------------------------------------------------------------------- - | Resetting Passwords - |-------------------------------------------------------------------------- - | - | Here you may set the options for resetting passwords including the view - | that is your password reset e-mail. You may also set the name of the - | table that maintains all of the reset tokens for your application. - | - | You may specify multiple password reset configurations if you have more - | than one user table or model in the application and you want to have - | separate password reset settings based on the specific user types. - | - | The expire time is the number of minutes that the reset token should be - | considered valid. This security feature keeps tokens short-lived so - | they have less time to be guessed. You may change this as needed. - | - */ - 'passwords' => [ 'users' => [ 'provider' => 'users', - 'email' => 'auth.emails.password', - 'table' => 'password_resets', - 'expire' => 60, + 'email' => 'emails.password', + 'table' => 'password_resets', + 'expire' => 60, ], ], From 723e46155978370bf8659755daa2bcc44c19c88c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 07:38:18 +0100 Subject: [PATCH 038/276] Fix logout. --- app/Http/routes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index 83c4ab6249..d334661479 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -534,7 +534,7 @@ Route::group( /** * Auth\Auth Controller */ - Route::get('/logout', ['uses' => 'Auth\AuthController@getLogout', 'as' => 'logout']); + Route::get('/logout', ['uses' => 'Auth\AuthController@logout', 'as' => 'logout']); } From 9fcb00f10bf43a3cd450f0f7c86e8aa87bdda379 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 07:46:11 +0100 Subject: [PATCH 039/276] Fixed some date range problems. --- app/Http/Controllers/AccountController.php | 1 - app/Http/Controllers/AttachmentController.php | 1 - app/Http/Controllers/BillController.php | 1 - app/Http/Controllers/BudgetController.php | 1 - app/Http/Controllers/CategoryController.php | 1 - app/Http/Kernel.php | 19 ++++++++++++++++++- app/Http/Middleware/Range.php | 9 +++++---- app/Http/routes.php | 2 +- 8 files changed, 24 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 4b043280fe..e018002bfc 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -26,7 +26,6 @@ class AccountController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('mainTitleIcon', 'fa-credit-card'); View::share('title', trans('firefly.accounts')); diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 41602ea21a..5a750ac786 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -29,7 +29,6 @@ class AttachmentController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('mainTitleIcon', 'fa-paperclip'); View::share('title', trans('firefly.attachments')); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index ed706b9315..ca59f0d256 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -24,7 +24,6 @@ class BillController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.bills')); View::share('mainTitleIcon', 'fa-calendar-o'); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index badaf90906..144cd44693 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -30,7 +30,6 @@ class BudgetController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.budgets')); View::share('mainTitleIcon', 'fa-tasks'); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 12b8272c75..d564789c21 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -29,7 +29,6 @@ class CategoryController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.categories')); View::share('mainTitleIcon', 'fa-bar-chart'); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index b4fcc1e54c..c8da84cf6b 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -25,13 +25,30 @@ class Kernel extends HttpKernel */ protected $middlewareGroups = [ - 'web' => [ + 'web' => [ \FireflyIII\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \FireflyIII\Http\Middleware\VerifyCsrfToken::class, ], + 'web-auth' => [ + \FireflyIII\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \FireflyIII\Http\Middleware\VerifyCsrfToken::class, + \FireflyIII\Http\Middleware\Authenticate::class, + ], + 'web-auth-range' => [ + \FireflyIII\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \FireflyIII\Http\Middleware\VerifyCsrfToken::class, + \FireflyIII\Http\Middleware\Authenticate::class, + \FireflyIII\Http\Middleware\Range::class, + ], 'api' => [ 'throttle:60,1', diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index ce0b27db58..72a2d567fe 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -6,12 +6,13 @@ namespace FireflyIII\Http\Middleware; use Carbon\Carbon; use Closure; use Illuminate\Contracts\Auth\Guard; -use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Navigation; use Preferences; use Session; use View; + /** * Class SessionFilter * @@ -41,14 +42,14 @@ class Range * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $theNext + * @param \Closure $next + * @param string|null $guard * * @return mixed */ public function handle($request, Closure $theNext, $guard = null) { - if ($this->auth->check()) { - + if (!Auth::guard($guard)->guest()) { // ignore preference. set the range to be the current month: if (!Session::has('start') && !Session::has('end')) { diff --git a/app/Http/routes.php b/app/Http/routes.php index d334661479..d24da4a4de 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -271,7 +271,7 @@ Route::group( Route::group( - ['middleware' => ['range', 'web']], function () { + ['middleware' => ['web-auth-range']], function () { /** * Home Controller From 4bc1c032bd179ea0ae812459bd085b76a8a1e6fd Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 07:48:45 +0100 Subject: [PATCH 040/276] Removed middle ware. --- app/Http/Controllers/Chart/AccountController.php | 1 - app/Http/Controllers/Chart/BillController.php | 1 - app/Http/Controllers/Chart/BudgetController.php | 1 - app/Http/Controllers/Chart/CategoryController.php | 1 - app/Http/Controllers/Chart/PiggyBankController.php | 1 - app/Http/Controllers/Chart/ReportController.php | 1 - app/Http/Controllers/Controller.php | 12 +++++++++--- app/Http/Controllers/CsvController.php | 1 - app/Http/Controllers/CurrencyController.php | 1 - app/Http/Controllers/HelpController.php | 1 - app/Http/Controllers/HomeController.php | 1 - app/Http/Controllers/JsonController.php | 1 - app/Http/Controllers/NewUserController.php | 1 - app/Http/Controllers/PiggyBankController.php | 1 - app/Http/Controllers/PreferencesController.php | 1 - app/Http/Controllers/ProfileController.php | 1 - app/Http/Controllers/ReportController.php | 1 - app/Http/Controllers/SearchController.php | 1 - app/Http/Controllers/TagController.php | 1 - app/Http/Controllers/TransactionController.php | 1 - 20 files changed, 9 insertions(+), 22 deletions(-) diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 0447f905b1..dc6fe0cf13 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -28,7 +28,6 @@ class AccountController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGenerator'); diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php index 340cf2d025..5f05b25c5c 100644 --- a/app/Http/Controllers/Chart/BillController.php +++ b/app/Http/Controllers/Chart/BillController.php @@ -27,7 +27,6 @@ class BillController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGenerator'); diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index dffd7dca38..39d3c4dffa 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -31,7 +31,6 @@ class BudgetController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator'); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 362d13e701..2314d61c74 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -31,7 +31,6 @@ class CategoryController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGenerator'); diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index 2c811f79d0..a6972d2a75 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -26,7 +26,6 @@ class PiggyBankController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator'); diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 74ac96f6b9..545fe46d7a 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -26,7 +26,6 @@ class ReportController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); // create chart generator: $this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGenerator'); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index ea8b4c3a99..813c1e88b1 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -3,14 +3,14 @@ namespace FireflyIII\Http\Controllers; use App; +use Auth; +use Carbon\Carbon; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; -use View; -use Auth; use Preferences; -use Carbon\Carbon; +use View; /** * Class Controller @@ -21,6 +21,12 @@ class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + /** @var string|\Symfony\Component\Translation\TranslatorInterface */ + protected $monthFormat; + + /** @var string|\Symfony\Component\Translation\TranslatorInterface */ + protected $monthAndDayFormat; + /** * Controller constructor. */ diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index a78df75634..bde7d5b7e0 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -34,7 +34,6 @@ class CsvController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.csv')); View::share('mainTitleIcon', 'fa-file-text-o'); diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index 3213934435..d2bdbf7e06 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -25,7 +25,6 @@ class CurrencyController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.currencies')); View::share('mainTitleIcon', 'fa-usd'); diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index 9c185c8dca..66beb18e60 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -13,7 +13,6 @@ class HelpController extends Controller { public function __construct() { - $this->middleware('auth'); parent::__construct(); } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index a0790dd9ba..df27f14735 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -20,7 +20,6 @@ class HomeController extends Controller { public function __construct() { - $this->middleware('auth'); parent::__construct(); } diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 28dc0df1f3..c379ea7139 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -22,7 +22,6 @@ class JsonController extends Controller { public function __construct() { - $this->middleware('auth'); parent::__construct(); } diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 37d905d42a..933b8bacf1 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -19,7 +19,6 @@ class NewUserController extends Controller { public function __construct() { - $this->middleware('auth'); parent::__construct(); } diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 799e2f3c3a..92ceb4cb88 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -31,7 +31,6 @@ class PiggyBankController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.piggyBanks')); View::share('mainTitleIcon', 'fa-sort-amount-asc'); diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index fd0f4f2a77..9cc7d3e861 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -20,7 +20,6 @@ class PreferencesController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.preferences')); View::share('mainTitleIcon', 'fa-gear'); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index fc1dce3869..d04be52dc5 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -17,7 +17,6 @@ class ProfileController extends Controller { public function __construct() { - $this->middleware('auth'); parent::__construct(); } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index b48566c11c..c462b879ce 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -27,7 +27,6 @@ class ReportController extends Controller */ public function __construct(ReportHelperInterface $helper) { - $this->middleware('auth'); parent::__construct(); $this->helper = $helper; diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 9f5dd863e0..6d9734a868 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -12,7 +12,6 @@ class SearchController extends Controller { public function __construct() { - $this->middleware('auth'); parent::__construct(); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 4402d44ec2..bf0baa0805 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -39,7 +39,6 @@ class TagController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', 'Tags'); View::share('mainTitleIcon', 'fa-tags'); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index b8f1d067a2..54e9b5fc2b 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -37,7 +37,6 @@ class TransactionController extends Controller */ public function __construct() { - $this->middleware('auth'); parent::__construct(); View::share('title', trans('firefly.transactions')); View::share('mainTitleIcon', 'fa-repeat'); From 9ff0b282f3e36e41a67531b8c046b1ed1d677f19 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 07:57:42 +0100 Subject: [PATCH 041/276] With the growing popularity and frankly, quality of this software, I've decided to connect my actual name to it in the form of a license. Soon, every file will have a short reference to this license. --- LICENSE | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..88ab8e15bb --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2016 Sander Dorigo + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 2980860377d70ca9c7ede9d7453ba92b01982b71 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 08:20:55 +0100 Subject: [PATCH 042/276] Did some code cleanup. Comments and headers mostly. --- app/Console/Kernel.php | 12 ++++ app/Events/Event.php | 5 ++ app/Exceptions/Handler.php | 31 +++++---- app/Helpers/Report/ReportHelper.php | 2 +- app/Http/Controllers/Auth/AuthController.php | 11 +++- .../Controllers/Auth/PasswordController.php | 6 +- app/Http/Controllers/HelpController.php | 3 + app/Http/Controllers/HomeController.php | 3 + app/Http/Controllers/JsonController.php | 3 + app/Http/Controllers/NewUserController.php | 3 + app/Http/Controllers/ProfileController.php | 3 + app/Http/Controllers/SearchController.php | 3 + app/Http/Kernel.php | 64 ++++++++++++------- app/Http/Middleware/Authenticate.php | 8 ++- app/Http/Middleware/EncryptCookies.php | 5 ++ app/Http/Middleware/Range.php | 3 +- .../Middleware/RedirectIfAuthenticated.php | 6 +- app/Http/Middleware/VerifyCsrfToken.php | 7 +- app/Http/Requests/Request.php | 5 ++ app/Jobs/Job.php | 5 ++ app/Models/Account.php | 42 ++++++------ app/Models/Role.php | 1 + app/Providers/AppServiceProvider.php | 5 ++ app/Providers/AuthServiceProvider.php | 5 ++ app/Providers/EventServiceProvider.php | 6 ++ app/Providers/RouteServiceProvider.php | 5 ++ .../Category/SingleCategoryRepository.php | 2 +- .../SingleCategoryRepositoryInterface.php | 2 +- app/Sql/Query.php | 2 +- app/User.php | 5 ++ storage/app/.gitignore | 2 +- storage/framework/cache/.gitignore | 2 +- 32 files changed, 198 insertions(+), 69 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 3a0a63ee81..9579812620 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -1,10 +1,22 @@ middleware('guest', ['except' => 'logout']); + parent::__construct(); } /** @@ -113,6 +118,8 @@ class AuthController extends Controller * * @param \Illuminate\Http\Request $request * + * @param $message + * * @return \Illuminate\Http\Response */ protected function sendFailedLoginResponse(Request $request, $message) @@ -129,6 +136,8 @@ class AuthController extends Controller /** * Get the failed login message. * + * @param $message + * * @return string */ protected function getFailedLoginMessage($message) diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php index 855f8895a9..855cfb5924 100755 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -10,6 +10,11 @@ use Illuminate\Mail\Message; use Illuminate\Support\Facades\Password; +/** + * Class PasswordController + * + * @package FireflyIII\Http\Controllers\Auth + */ class PasswordController extends Controller { /* @@ -28,7 +33,6 @@ class PasswordController extends Controller /** * Create a new password controller instance. * - * @return void */ public function __construct() { diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index 66beb18e60..667aef28f3 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -11,6 +11,9 @@ use Response; */ class HelpController extends Controller { + /** + * HelpController constructor. + */ public function __construct() { parent::__construct(); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index df27f14735..1d638813da 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -18,6 +18,9 @@ use Steam; */ class HomeController extends Controller { + /** + * HomeController constructor. + */ public function __construct() { parent::__construct(); diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index c379ea7139..c94f886cf6 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -20,6 +20,9 @@ use Session; */ class JsonController extends Controller { + /** + * JsonController constructor. + */ public function __construct() { parent::__construct(); diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 933b8bacf1..58a68a45fa 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -17,6 +17,9 @@ use View; */ class NewUserController extends Controller { + /** + * NewUserController constructor. + */ public function __construct() { parent::__construct(); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index d04be52dc5..684d9e2d98 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -15,6 +15,9 @@ use Session; */ class ProfileController extends Controller { + /** + * ProfileController constructor. + */ public function __construct() { parent::__construct(); diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 6d9734a868..e9793687f3 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -10,6 +10,9 @@ use Input; */ class SearchController extends Controller { + /** + * SearchController constructor. + */ public function __construct() { parent::__construct(); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c8da84cf6b..32625a3528 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,8 +2,24 @@ namespace FireflyIII\Http; +use FireflyIII\Http\Middleware\Authenticate; +use FireflyIII\Http\Middleware\EncryptCookies; +use FireflyIII\Http\Middleware\Range; +use FireflyIII\Http\Middleware\RedirectIfAuthenticated; +use FireflyIII\Http\Middleware\VerifyCsrfToken; +use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth; +use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Foundation\Http\Kernel as HttpKernel; +use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode; +use Illuminate\Routing\Middleware\ThrottleRequests; +use Illuminate\Session\Middleware\StartSession; +use Illuminate\View\Middleware\ShareErrorsFromSession; +/** + * Class Kernel + * + * @package FireflyIII\Http + */ class Kernel extends HttpKernel { /** @@ -15,7 +31,7 @@ class Kernel extends HttpKernel */ protected $middleware = [ - \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, + CheckForMaintenanceMode::class, ]; /** @@ -26,28 +42,28 @@ class Kernel extends HttpKernel protected $middlewareGroups = [ 'web' => [ - \FireflyIII\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \FireflyIII\Http\Middleware\VerifyCsrfToken::class, + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, ], 'web-auth' => [ - \FireflyIII\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \FireflyIII\Http\Middleware\VerifyCsrfToken::class, - \FireflyIII\Http\Middleware\Authenticate::class, + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + Authenticate::class, ], 'web-auth-range' => [ - \FireflyIII\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \FireflyIII\Http\Middleware\VerifyCsrfToken::class, - \FireflyIII\Http\Middleware\Authenticate::class, - \FireflyIII\Http\Middleware\Range::class, + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + Authenticate::class, + Range::class, ], 'api' => [ @@ -64,10 +80,10 @@ class Kernel extends HttpKernel */ protected $routeMiddleware = [ - 'auth' => \FireflyIII\Http\Middleware\Authenticate::class, - 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'guest' => \FireflyIII\Http\Middleware\RedirectIfAuthenticated::class, - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - 'range' => \FireflyIII\Http\Middleware\Range::class, + 'auth' => Authenticate::class, + 'auth.basic' => AuthenticateWithBasicAuth::class, + 'guest' => RedirectIfAuthenticated::class, + 'throttle' => ThrottleRequests::class, + 'range' => Range::class, ]; } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index aea4c9bbf0..7032eb9bf2 100755 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -2,12 +2,14 @@ namespace FireflyIII\Http\Middleware; -use App; -use Carbon\Carbon; use Closure; use Illuminate\Support\Facades\Auth; -use Preferences; +/** + * Class Authenticate + * + * @package FireflyIII\Http\Middleware + */ class Authenticate { /** diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index d863b91383..2686edac4f 100755 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -4,6 +4,11 @@ namespace FireflyIII\Http\Middleware; use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter; +/** + * Class EncryptCookies + * + * @package FireflyIII\Http\Middleware + */ class EncryptCookies extends BaseEncrypter { /** diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 72a2d567fe..a4b6ead33d 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -42,10 +42,11 @@ class Range * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param Closure $theNext * @param string|null $guard * * @return mixed + * @internal param Closure $next */ public function handle($request, Closure $theNext, $guard = null) { diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 5fbd2d05b2..1cf76e8aa8 100755 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -4,8 +4,12 @@ namespace FireflyIII\Http\Middleware; use Closure; use Illuminate\Support\Facades\Auth; -use Log; +/** + * Class RedirectIfAuthenticated + * + * @package FireflyIII\Http\Middleware + */ class RedirectIfAuthenticated { /** diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 89a57ce341..413dde74b3 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -3,6 +3,11 @@ namespace FireflyIII\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier; +/** + * Class VerifyCsrfToken + * + * @package FireflyIII\Http\Middleware + */ class VerifyCsrfToken extends BaseVerifier { /** @@ -14,4 +19,4 @@ class VerifyCsrfToken extends BaseVerifier = [ // ]; -} \ No newline at end of file +} diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 3bc8daac17..8f7d18805c 100755 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -4,6 +4,11 @@ namespace FireflyIII\Http\Requests; use Illuminate\Foundation\Http\FormRequest; +/** + * Class Request + * + * @package FireflyIII\Http\Requests + */ abstract class Request extends FormRequest { // diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php index 82f3af1c2c..264fe0f180 100755 --- a/app/Jobs/Job.php +++ b/app/Jobs/Job.php @@ -4,6 +4,11 @@ namespace FireflyIII\Jobs; use Illuminate\Bus\Queueable; +/** + * Class Job + * + * @package FireflyIII\Jobs + */ abstract class Job { /* diff --git a/app/Models/Account.php b/app/Models/Account.php index 91f75cb286..4f10465e79 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -11,28 +11,32 @@ use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\JoinClause; use Watson\Validating\ValidatingTrait; + /** * FireflyIII\Models\Account * - * @property integer $id - * @property Carbon $created_at - * @property Carbon $updated_at - * @property Carbon $deleted_at - * @property integer $user_id - * @property integer $account_type_id - * @property string $name - * @property boolean $active - * @property boolean $encrypted - * @property float $virtual_balance - * @property string $iban - * @property-read Collection|AccountMeta[] $accountMeta - * @property-read AccountType $accountType - * @property-read mixed $name_for_editform - * @property-read Collection|PiggyBank[] $piggyBanks - * @property-read Collection|Transaction[] $transactions - * @property-read User $user - * @method static Builder|Account accountTypeIn($types) - * @method static Builder|Account hasMetaValue($name, $value) + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Carbon\Carbon $deleted_at + * @property integer $user_id + * @property integer $account_type_id + * @property string $name + * @property boolean $active + * @property boolean $encrypted + * @property float $virtual_balance + * @property string $iban + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\AccountMeta[] $accountMeta + * @property-read \FireflyIII\Models\AccountType $accountType + * @property-read mixed $name_for_editform + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions + * @property-read \FireflyIII\User $user + * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account accountTypeIn($types) + * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account hasMetaValue($name, $value) + * @property string $startBalance + * @property string $endBalance + * */ class Account extends Model { diff --git a/app/Models/Role.php b/app/Models/Role.php index 47af6ddcf2..6eaa81e92b 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -20,4 +20,5 @@ use Zizaco\Entrust\EntrustRole; */ class Role extends EntrustRole { + } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 73d79bc734..ca50e9b7ee 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,11 @@ namespace FireflyIII\Providers; use Illuminate\Support\ServiceProvider; +/** + * Class AppServiceProvider + * + * @package FireflyIII\Providers + */ class AppServiceProvider extends ServiceProvider { /** diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index da063968ec..0666667ec6 100755 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -5,6 +5,11 @@ namespace FireflyIII\Providers; use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +/** + * Class AuthServiceProvider + * + * @package FireflyIII\Providers + */ class AuthServiceProvider extends ServiceProvider { /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 6bea8ab171..e76c561829 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -4,6 +4,7 @@ namespace FireflyIII\Providers; use FireflyIII\Models\Account; use FireflyIII\Models\BudgetLimit; +use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\Transaction; @@ -14,6 +15,11 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi use Log; use Navigation; +/** + * Class EventServiceProvider + * + * @package FireflyIII\Providers + */ class EventServiceProvider extends ServiceProvider { /** diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index d9a1761eae..34cdcf76c6 100755 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -5,6 +5,11 @@ namespace FireflyIII\Providers; use Illuminate\Routing\Router; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; +/** + * Class RouteServiceProvider + * + * @package FireflyIII\Providers + */ class RouteServiceProvider extends ServiceProvider { /** diff --git a/app/Repositories/Category/SingleCategoryRepository.php b/app/Repositories/Category/SingleCategoryRepository.php index 320c78e7f0..3b52f6c04b 100644 --- a/app/Repositories/Category/SingleCategoryRepository.php +++ b/app/Repositories/Category/SingleCategoryRepository.php @@ -235,4 +235,4 @@ class SingleCategoryRepository extends ComponentRepository implements SingleCate } -} \ No newline at end of file +} diff --git a/app/Repositories/Category/SingleCategoryRepositoryInterface.php b/app/Repositories/Category/SingleCategoryRepositoryInterface.php index 6e6b4b76c2..cae94328d4 100644 --- a/app/Repositories/Category/SingleCategoryRepositoryInterface.php +++ b/app/Repositories/Category/SingleCategoryRepositoryInterface.php @@ -119,4 +119,4 @@ interface SingleCategoryRepositoryInterface * @return Category */ public function update(Category $category, array $data); -} \ No newline at end of file +} diff --git a/app/Sql/Query.php b/app/Sql/Query.php index b0989a82d7..8abf2376e1 100644 --- a/app/Sql/Query.php +++ b/app/Sql/Query.php @@ -14,4 +14,4 @@ class Query const SPENT = 1; const EARNED = 2; -} \ No newline at end of file +} diff --git a/app/User.php b/app/User.php index 606030315e..7e4c23b417 100755 --- a/app/User.php +++ b/app/User.php @@ -4,6 +4,11 @@ namespace FireflyIII; use Illuminate\Foundation\Auth\User as Authenticatable; +/** + * Class User + * + * @package FireflyIII + */ class User extends Authenticatable { /** diff --git a/storage/app/.gitignore b/storage/app/.gitignore index c96a04f008..d6b7ef32c8 100755 --- a/storage/app/.gitignore +++ b/storage/app/.gitignore @@ -1,2 +1,2 @@ * -!.gitignore \ No newline at end of file +!.gitignore diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore index c96a04f008..d6b7ef32c8 100755 --- a/storage/framework/cache/.gitignore +++ b/storage/framework/cache/.gitignore @@ -1,2 +1,2 @@ * -!.gitignore \ No newline at end of file +!.gitignore From cb5fa401cb02b7bef59c87fd5238f06c336a0fe4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 08:36:50 +0100 Subject: [PATCH 043/276] Try to get travis working again. --- .env.testing | 36 ++++++++++++++++++++++++++++++++++++ .travis.yml | 4 ++-- 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100755 .env.testing diff --git a/.env.testing b/.env.testing new file mode 100755 index 0000000000..f070b244fc --- /dev/null +++ b/.env.testing @@ -0,0 +1,36 @@ +APP_ENV=testing +APP_DEBUG=true +APP_KEY=SomeRandomStringOf32CharsExactly + + +DB_CONNECTION=sqlite +DB_HOST=localhost +DB_DATABASE=homestead +DB_USERNAME=homestead +DB_PASSWORD=secret + +CACHE_DRIVER=array +SESSION_DRIVER=array +QUEUE_DRIVER=array + +DEFAULT_CURRENCY=EUR +DEFAULT_LANGUAGE=en_US + +REDIS_HOST=localhost +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=log +MAIL_HOST=mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null + +SHOW_INCOMPLETE_TRANSLATIONS=false + +ANALYTICS_ID=abcde +RUNCLEANUP=false +SITE_OWNER=mail@example.com + +BLOCKED_DOMAINS= \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 383b324dc1..abb00b138e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,10 @@ php: - 5.6 install: - - composer update + - composer install - php artisan env - mv -v .env.testing .env - - touch storage/database/testing.db + - touch storage/database.sqlite - php artisan migrate --env=testing - php artisan migrate --seed --env=testing From 5e744390c08c9dff5d79852de75b99209cd8e708 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 08:45:27 +0100 Subject: [PATCH 044/276] Remove PHP requirement. --- composer.json | 1 - composer.lock | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 8543ca1420..44c5ab0766 100755 --- a/composer.json +++ b/composer.json @@ -14,7 +14,6 @@ }], "require": { - "php": ">=5.6.11", "laravel/framework": "5.2.*", "davejamesmiller/laravel-breadcrumbs": "~3.0", "watson/validating": "~2.0", diff --git a/composer.lock b/composer.lock index a7e88bbdc1..b48e815f45 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "1dbccadc167d6bc59012345c9873722e", - "content-hash": "ab9579d4e3b4d532ff42f82aa009f096", + "hash": "847987de4271f2467a4a5fc50bc04cc9", + "content-hash": "28b178f07a713b4db441e7e1f380916e", "packages": [ { "name": "classpreloader/classpreloader", @@ -4114,8 +4114,6 @@ }, "prefer-stable": false, "prefer-lowest": false, - "platform": { - "php": ">=5.6.11" - }, + "platform": [], "platform-dev": [] } From 5e5fdfdd5133ec21bbd21725f1f4963743fb80fc Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 08:47:54 +0100 Subject: [PATCH 045/276] Move database reference. --- config/database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/database.php b/config/database.php index 66e88a9090..5865a2f758 100755 --- a/config/database.php +++ b/config/database.php @@ -48,7 +48,7 @@ return [ 'sqlite' => [ 'driver' => 'sqlite', - 'database' => database_path('database.sqlite'), + 'database' => storage_path('database.sqlite'), 'prefix' => '', ], From 05f8773fa0a0e5cbeb726830c81dfe486914ca2c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 08:51:49 +0100 Subject: [PATCH 046/276] Fix user model. --- app/User.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/User.php b/app/User.php index 7e4c23b417..e8b52afc07 100755 --- a/app/User.php +++ b/app/User.php @@ -3,6 +3,7 @@ namespace FireflyIII; use Illuminate\Foundation\Auth\User as Authenticatable; +use Zizaco\Entrust\Traits\EntrustUserTrait; /** * Class User @@ -11,6 +12,8 @@ use Illuminate\Foundation\Auth\User as Authenticatable; */ class User extends Authenticatable { + use EntrustUserTrait; + /** * The attributes that are mass assignable. * From 4a2768f8d1cdc43e6f00ac763a674459d4fb0ebc Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 09:56:41 +0100 Subject: [PATCH 047/276] Reinstated test files. --- tests/BasicTest.php | 18 ++++++++++ tests/unit/Models/TransactionTypeTest.php | 41 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/BasicTest.php create mode 100644 tests/unit/Models/TransactionTypeTest.php diff --git a/tests/BasicTest.php b/tests/BasicTest.php new file mode 100644 index 0000000000..0f1b9f46d4 --- /dev/null +++ b/tests/BasicTest.php @@ -0,0 +1,18 @@ +assertTrue(true); + } +} diff --git a/tests/unit/Models/TransactionTypeTest.php b/tests/unit/Models/TransactionTypeTest.php new file mode 100644 index 0000000000..0a0daedcb9 --- /dev/null +++ b/tests/unit/Models/TransactionTypeTest.php @@ -0,0 +1,41 @@ +first(); + $this->assertTrue($transactionType->isWithdrawal()); + } + + public function testIsDeposit() + { + $transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); + $this->assertTrue($transactionType->isDeposit()); + } + + public function testIsTransfer() + { + $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); + $this->assertTrue($transactionType->isTransfer()); + } + + public function testIsOpeningBalance() + { + $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); + $this->assertTrue($transactionType->isOpeningBalance()); + } +} From 681bc580c4eb2617310dc7f80b324ed614b9f88f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 12:54:43 +0100 Subject: [PATCH 048/276] Removed GA beacon. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c903b7412..9133babbbd 100644 --- a/README.md +++ b/README.md @@ -126,5 +126,4 @@ If you like this tool, feel free to [donate me some beer money](https://www.payp [![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) [![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii) [![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) -[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii) -![GA](https://ga-beacon.appspot.com/UA-58172398-6/firefly-iii/readme) +[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii) \ No newline at end of file From 9f9d74440641fff4770d5420a9057792aa4d39df Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 14:33:21 +0100 Subject: [PATCH 049/276] Cleared read me. [skip-ci] --- README.md | 123 +++--------------------------------------------------- 1 file changed, 6 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 9133babbbd..b39a75a39c 100644 --- a/README.md +++ b/README.md @@ -2,128 +2,17 @@ [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads)](https://packagist.org/packages/grumpydictator/firefly-iii) - [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) [![Build Status](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/build.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master) +[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) +[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii) +[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) +[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii) + ## About "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. - -Personal financial management is pretty difficult, and everybody has their own approach to it. Some people -make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase -their current cashflow. There are tons of ways to save and earn money. -Firefly works on the principle that if you know where you're money is going, you can stop it from going there. - -To get to know Firefly, and to see if it fits you, check out these resources: - -- The screenshots below on this very page. -- The featurelist below, also on this very page. -- The [full description](https://github.com/JC5/firefly-iii/wiki/full-description), which will tell you how Firefly works, -and the philosophy behind it. - -#### A quick technical overview - -Firefly is a system you'll have install yourself on webhosting of your choosing. It needs PHP and MySQL. The current version of Firefly III requires PHP 5.6.4 or -higher. Soon, this will be PHP 7.0.0 or higher. - - -#### About the name (should you care) - -It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since. - -## Current features - -- [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system); -- You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management; -- You can manage different types of accounts; - - [Asset](https://en.wikipedia.org/wiki/Asset) accounts - - Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household)) - - Saving accounts - - Credit cards -- It's possible to create, change and manage money using _[budgets](https://en.wikipedia.org/wiki/Envelope_system)_; -- Organize transactions using categories; -- Save towards a goal using [piggy banks](https://en.wikipedia.org/wiki/Piggy_bank); -- Predict and anticipate [bills](https://en.wikipedia.org/wiki/Invoice); -- View income / expense [reports](https://en.wikipedia.org/wiki/Financial_statement); -- Organize expenses using tags; -- Lots of help text in case you don't get it. - -Everything is organised: - -- Clear views that should show you how you're doing; -- Easy navigation through your records; -- Browse back and forth to see previous months or even years; -- Lots of charts because we all love them; -- Financial reporting showing you how well you are doing. - -## Screenshots - -_Please note that everything in these screenshots is fictional and may not be realistic._ - -![Index](https://i.nder.be/hmp5mhw5) - -![Accounts](https://i.nder.be/hf5k02g9) - -![Budgets](https://i.nder.be/gzv635mz) - -![Reports 1](https://i.nder.be/g0w698s3) - -![Reports 2](https://i.nder.be/cr77nyxq) - -![Bills](https://i.nder.be/c7sugsz5) - -![Piggy banks](https://i.nder.be/gy2nk0y4) - -## Running and installing - -If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation), -[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable) -and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**. - -If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/). -This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one month. It's a trial. - -## Security - -You should always run Firefly III on a site with TLS enabled (https://). Please note that although some parts of the -database are encrypted (transaction descriptions, names, etc.) some parts are _not_ (amounts, dates, etc). If you need -more security, you must enable transparent database encryption or a comparable technology. Please remember that this -is open source software under active development, and it is in no way guaranteed to be safe or secure. - -## Translations - -Firefly III is currently available in Dutch and English. Support for other languages is being worked on. I could use -your help. Checkout [Crowdin](https://crowdin.com/project/firefly-iii) for more information. - -## Credits - -Firefly III uses the following libraries and tools: - -* The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/) -* The [Google charts](https://developers.google.com/chart/) library. -* [Chart.js](http://www.chartjs.org/) -* [Bootstrap](http://getbootstrap.com/) -* [Laravel](http://laravel.com/) -* [Twig](http://twig.sensiolabs.org/) -* For development, some of the excellent tools made by [Barry van den Heuvel](https://github.com/barryvdh) -* [Bootstrap sortable](https://github.com/drvic10k/bootstrap-sortable) by [Matúš BrliÅ¥](https://github.com/drvic10k). -* [Date range picker](https://github.com/dangrossman/bootstrap-daterangepicker/) by [Dan Grossman](https://github.com/dangrossman) -* The [real favicon generator](http://realfavicongenerator.net/) -* Various other open source components (see [composer.json](https://github.com/JC5/firefly-iii/blob/master/composer.json)) - - -## Current state - -Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. Translations are a work in progress. - -Questions, ideas, bugs or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)! - -If you like this tool, feel free to [donate me some beer money](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2ZMV952UUSCLU&lc=NL&item_name=Development%20of%20Firefly¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted). - -[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) -[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii) -[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) -[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii) \ No newline at end of file + \ No newline at end of file From b311ef70bc16b76a71e46e544d98a32598ddca61 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 14:34:28 +0100 Subject: [PATCH 050/276] Point to website. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index b39a75a39c..6b1fbe01b7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) [![Build Status](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/build.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master) - [![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) [![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii) [![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) @@ -15,4 +14,4 @@ "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. - \ No newline at end of file +**[Please checkout the website for more information.](https://jc5.github.io/firefly-iii/)** \ No newline at end of file From af29b31ea8b1c76f3668ecbd69b17848a803fcad Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:38:48 +0100 Subject: [PATCH 051/276] Remove "created by" message. --- app/Helpers/Csv/PostProcessing/PostProcessorInterface.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php b/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php index da9877cc8b..88c0b60559 100644 --- a/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php +++ b/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php @@ -1,10 +1,4 @@ Date: Sat, 9 Jan 2016 15:39:02 +0100 Subject: [PATCH 052/276] New middleware. --- app/Http/Kernel.php | 2 ++ app/Http/Middleware/Binder.php | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 app/Http/Middleware/Binder.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 32625a3528..3c1c958dec 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -3,6 +3,7 @@ namespace FireflyIII\Http; use FireflyIII\Http\Middleware\Authenticate; +use FireflyIII\Http\Middleware\Binder; use FireflyIII\Http\Middleware\EncryptCookies; use FireflyIII\Http\Middleware\Range; use FireflyIII\Http\Middleware\RedirectIfAuthenticated; @@ -64,6 +65,7 @@ class Kernel extends HttpKernel VerifyCsrfToken::class, Authenticate::class, Range::class, + Binder::class, ], 'api' => [ diff --git a/app/Http/Middleware/Binder.php b/app/Http/Middleware/Binder.php new file mode 100644 index 0000000000..3d13b49de5 --- /dev/null +++ b/app/Http/Middleware/Binder.php @@ -0,0 +1,50 @@ +binders = Domain::getBindables(); + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * + * @return mixed + */ + public function handle($request, Closure $next) + { + foreach ($request->route()->parameters() as $key => $value) { + if (isset($this->binders[$key])) { + $boundObject = $this->performBinding($key, $value, $request->route()); + $request->route()->setParameter($key, $boundObject); + } + } + + return $next($request); + + //return $next($request); + } + + /** + * @param $key + * @param $value + * @param $route + * + * @return mixed + */ + private function performBinding($key, $value, $route) + { + return $this->binders[$key]::routeBinder($value, $route); + } +} From a14544398b138bd25631984e775bcc465c26531c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:39:34 +0100 Subject: [PATCH 053/276] New middleware --- app/Http/routes.php | 75 -------------------------- app/Models/Account.php | 50 +++++++++-------- app/Support/Binder/AccountList.php | 50 +++++++++++++++++ app/Support/Binder/BinderInterface.php | 27 ++++++++++ app/Support/Binder/Date.php | 43 +++++++++++++++ app/Support/Domain.php | 32 +++++++++++ 6 files changed, 181 insertions(+), 96 deletions(-) create mode 100644 app/Support/Binder/AccountList.php create mode 100644 app/Support/Binder/BinderInterface.php create mode 100644 app/Support/Binder/Date.php create mode 100644 app/Support/Domain.php diff --git a/app/Http/routes.php b/app/Http/routes.php index d24da4a4de..0bb2848285 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -12,43 +12,6 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -// models -Route::bind( - 'account', - function ($value) { - if (Auth::check()) { - $object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->where('account_types.editable', 1) - ->where('accounts.id', $value) - ->where('user_id', Auth::user()->id) - ->first(['accounts.*']); - if ($object) { - return $object; - } - } - throw new NotFoundHttpException; - } -); -// accounts -Route::bind( - 'accountList', - function ($value) { - if (Auth::check()) { - $ids = explode(',', $value); - /** @var \Illuminate\Support\Collection $object */ - $object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->where('account_types.editable', 1) - ->whereIn('accounts.id', $ids) - ->where('user_id', Auth::user()->id) - ->get(['accounts.*']); - if ($object->count() > 0) { - return $object; - } - } - throw new NotFoundHttpException; - } -); // budget list Route::bind( 'budgetList', @@ -98,44 +61,6 @@ Route::bind( } ); -// Date -Route::bind( - 'start_date', - function ($value) { - if (Auth::check()) { - - try { - $date = new Carbon($value); - } catch (Exception $e) { - Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id); - throw new NotFoundHttpException; - } - - return $date; - } - throw new NotFoundHttpException; - } -); - -// Date -Route::bind( - 'end_date', - function ($value) { - if (Auth::check()) { - - try { - $date = new Carbon($value); - } catch (Exception $e) { - Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id); - throw new NotFoundHttpException; - } - - return $date; - } - throw new NotFoundHttpException; - } -); - Route::bind( 'tj', function ($value) { if (Auth::check()) { diff --git a/app/Models/Account.php b/app/Models/Account.php index 4f10465e79..ff22ff5fdb 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -1,41 +1,39 @@ belongsTo('FireflyIII\User'); } + public static function routeBinder(Account $value) + { + + if (Auth::check()) { + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } } diff --git a/app/Support/Binder/AccountList.php b/app/Support/Binder/AccountList.php new file mode 100644 index 0000000000..59de12db74 --- /dev/null +++ b/app/Support/Binder/AccountList.php @@ -0,0 +1,50 @@ +where('account_types.editable', 1) + ->whereIn('accounts.id', $ids) + ->where('user_id', Auth::user()->id) + ->get(['accounts.*']); + if ($object->count() > 0) { + return $object; + } + } + throw new NotFoundHttpException; + } +} \ No newline at end of file diff --git a/app/Support/Binder/BinderInterface.php b/app/Support/Binder/BinderInterface.php new file mode 100644 index 0000000000..aba12d6455 --- /dev/null +++ b/app/Support/Binder/BinderInterface.php @@ -0,0 +1,27 @@ +id); + throw new NotFoundHttpException; + } + + return $date; + } +} \ No newline at end of file diff --git a/app/Support/Domain.php b/app/Support/Domain.php new file mode 100644 index 0000000000..a3e13fe3f2 --- /dev/null +++ b/app/Support/Domain.php @@ -0,0 +1,32 @@ + 'FireflyIII\Models\Account', + 'accountList' => 'FireflyIII\Support\Binder\AccountList', + 'start_date' => 'FireflyIII\Support\Binder\Date', + 'end_date' => 'FireflyIII\Support\Binder\Date', + ]; + } + +} \ No newline at end of file From 397c4926ebe36931f4047c22756b5fddd2095f85 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:39:41 +0100 Subject: [PATCH 054/276] Update cookie policy --- config/session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/session.php b/config/session.php index 69aac40867..e90989cd8a 100755 --- a/config/session.php +++ b/config/session.php @@ -148,6 +148,6 @@ return [ | */ - 'secure' => true, + 'secure' => false, ]; From 5fc7cafcbe1281789954b6d825bfaabac482783f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:44:07 +0100 Subject: [PATCH 055/276] All new binders. --- app/Support/Domain.php | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/app/Support/Domain.php b/app/Support/Domain.php index a3e13fe3f2..198a08bbc6 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -22,11 +22,28 @@ class Domain public static function getBindables() { return [ - 'account' => 'FireflyIII\Models\Account', - 'accountList' => 'FireflyIII\Support\Binder\AccountList', - 'start_date' => 'FireflyIII\Support\Binder\Date', - 'end_date' => 'FireflyIII\Support\Binder\Date', + // models + 'account' => 'FireflyIII\Models\Account', + 'attachment' => 'FireflyIII\Models\Attachment', + 'bill' => 'FireflyIII\Models\Bill', + 'budget' => 'FireflyIII\Models\Budget', + 'category' => 'FireflyIII\Models\Category', + 'currency' => 'FireflyIII\Models\Currency', + 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', + 'piggyBank' => 'FireflyIII\Models\PiggyBank', + 'tj' => 'FireflyIII\Models\TransactionJournal', + 'tag' => 'FireflyIII\Models\Tag', + // lists + 'accountList' => 'FireflyIII\Support\Binder\AccountList', + 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', + 'categoryList' => 'FireflyIII\Support\Binder\CategoryList', + + // others + 'start_date' => 'FireflyIII\Support\Binder\Date', + 'end_date' => 'FireflyIII\Support\Binder\Date' ]; + + } } \ No newline at end of file From 1f263f60a78ae310b924fae7a6889ee1b6439e26 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:45:21 +0100 Subject: [PATCH 056/276] New binder for category lists. --- app/Support/Binder/CategoryList.php | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 app/Support/Binder/CategoryList.php diff --git a/app/Support/Binder/CategoryList.php b/app/Support/Binder/CategoryList.php new file mode 100644 index 0000000000..ff787b85b5 --- /dev/null +++ b/app/Support/Binder/CategoryList.php @@ -0,0 +1,51 @@ +where('user_id', Auth::user()->id) + ->get(); + + // add empty budget if applicable. + if (in_array('0', $ids)) { + $object->push(new Category); + } + + if ($object->count() > 0) { + return $object; + } + } + throw new NotFoundHttpException; + } +} \ No newline at end of file From ef4e964c94f1879b30fda72de747a2eb2d11f185 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:47:03 +0100 Subject: [PATCH 057/276] Fixed budget list binder. --- app/Support/Binder/BudgetList.php | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 app/Support/Binder/BudgetList.php diff --git a/app/Support/Binder/BudgetList.php b/app/Support/Binder/BudgetList.php new file mode 100644 index 0000000000..9961672400 --- /dev/null +++ b/app/Support/Binder/BudgetList.php @@ -0,0 +1,52 @@ +whereIn('id', $ids) + ->where('user_id', Auth::user()->id) + ->get(); + + // add empty budget if applicable. + if (in_array('0', $ids)) { + $object->push(new Budget); + } + + if ($object->count() > 0) { + return $object; + } + } + throw new NotFoundHttpException; + } +} \ No newline at end of file From caa1ff120a490b6ce56933ac4d7aa62080a98641 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 15:53:11 +0100 Subject: [PATCH 058/276] Built more binders. --- app/Http/Middleware/Binder.php | 3 +++ app/Models/Attachment.php | 13 +++++++++++++ app/Models/TransactionJournal.php | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/app/Http/Middleware/Binder.php b/app/Http/Middleware/Binder.php index 3d13b49de5..87fa2fe0ff 100644 --- a/app/Http/Middleware/Binder.php +++ b/app/Http/Middleware/Binder.php @@ -9,6 +9,9 @@ class Binder { protected $binders = []; + /** + * Binder constructor. + */ public function __construct() { $this->binders = Domain::getBindables(); diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index c73424d76a..4d90fc06a1 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -2,11 +2,13 @@ namespace FireflyIII\Models; +use Auth; use Carbon\Carbon; use Crypt; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Attachment @@ -173,4 +175,15 @@ class Attachment extends Model $this->attributes['notes'] = Crypt::encrypt($value); } + public static function routeBinder(Attachment $value) + { + if (Auth::check()) { + + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + } diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index ab4ed569ec..07f4048b47 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -1,5 +1,6 @@ transactionType->isOpeningBalance(); } + + /** + * @param $value + * @param $route + * + * @return mixed + * @throws NotFoundHttpException + */ + public static function routeBinder($value, $route) + { + if (Auth::check()) { + $object = TransactionJournal::where('id', $value)->where('user_id', Auth::user()->id)->first(); + if ($object) { + return $object; + } + } + + throw new NotFoundHttpException; + } } From 29145bf6cf02c3ba83fc5dca80c66b06104fde8a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 16:09:26 +0100 Subject: [PATCH 059/276] More bindings. --- app/Http/routes.php | 173 ----------------------------- app/Models/Bill.php | 13 +++ app/Models/Budget.php | 12 ++ app/Models/Category.php | 12 ++ app/Models/LimitRepetition.php | 18 +++ app/Models/PiggyBank.php | 12 ++ app/Models/Tag.php | 15 +++ app/Models/TransactionCurrency.php | 13 +++ app/Support/Domain.php | 2 +- 9 files changed, 96 insertions(+), 174 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index 0bb2848285..01fb4e9160 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -12,179 +12,6 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -// budget list -Route::bind( - 'budgetList', - function ($value) { - if (Auth::check()) { - $ids = explode(',', $value); - /** @var \Illuminate\Support\Collection $object */ - $object = Budget::where('active', 1) - ->whereIn('id', $ids) - ->where('user_id', Auth::user()->id) - ->get(); - - // add empty budget if applicable. - if (in_array('0', $ids)) { - $object->push(new Budget); - } - - if ($object->count() > 0) { - return $object; - } - } - throw new NotFoundHttpException; - } -); - -// category list -Route::bind( - 'categoryList', - function ($value) { - if (Auth::check()) { - $ids = explode(',', $value); - /** @var \Illuminate\Support\Collection $object */ - $object = Category::whereIn('id', $ids) - ->where('user_id', Auth::user()->id) - ->get(); - - // add empty budget if applicable. - if (in_array('0', $ids)) { - $object->push(new Category); - } - - if ($object->count() > 0) { - return $object; - } - } - throw new NotFoundHttpException; - } -); - -Route::bind( - 'tj', function ($value) { - if (Auth::check()) { - $object = TransactionJournal::where('id', $value)->where('user_id', Auth::user()->id)->first(); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'attachment', function ($value) { - if (Auth::check()) { - $object = Attachment::where('id', $value)->where('user_id', Auth::user()->id)->first(); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'currency', function ($value) { - if (Auth::check()) { - $object = TransactionCurrency::find($value); - if ($object) { - return $object; - } - } - throw new NotFoundHttpException; -} -); - -Route::bind( - 'bill', function ($value) { - if (Auth::check()) { - $object = Bill::where('id', $value)->where('user_id', Auth::user()->id)->first(); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'budget', function ($value) { - if (Auth::check()) { - $object = Budget::where('id', $value)->where('user_id', Auth::user()->id)->first(); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'limitrepetition', function ($value) { - if (Auth::check()) { - $object = LimitRepetition::where('limit_repetitions.id', $value) - ->leftjoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id') - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('budgets.user_id', Auth::user()->id) - ->first(['limit_repetitions.*']); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'piggyBank', function ($value) { - if (Auth::check()) { - $object = PiggyBank::where('piggy_banks.id', $value) - ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('accounts.user_id', Auth::user()->id) - ->first(['piggy_banks.*']); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'category', function ($value) { - if (Auth::check()) { - $object = Category::where('id', $value)->where('user_id', Auth::user()->id)->first(); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - -Route::bind( - 'tag', function ($value) { - if (Auth::check()) { - $object = Tag::where('id', $value)->where('user_id', Auth::user()->id)->first(); - if ($object) { - return $object; - } - } - - throw new NotFoundHttpException; -} -); - - // auth routes, i think Route::group( ['middleware' => 'web'], function () { diff --git a/app/Models/Bill.php b/app/Models/Bill.php index c148f88a10..79cf30bbfe 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -1,10 +1,12 @@ user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + + } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 26f085a118..4000306a2c 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -1,11 +1,13 @@ belongsTo('FireflyIII\User'); } + public static function routeBinder(Budget $value) + { + if (Auth::check()) { + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + } diff --git a/app/Models/Category.php b/app/Models/Category.php index 4ddb31a7a7..cca73fe416 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -1,11 +1,13 @@ belongsTo('FireflyIII\User'); } + public static function routeBinder(Category $value) + { + if (Auth::check()) { + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + } diff --git a/app/Models/LimitRepetition.php b/app/Models/LimitRepetition.php index 4009e6479b..bd91fbf8a6 100644 --- a/app/Models/LimitRepetition.php +++ b/app/Models/LimitRepetition.php @@ -1,7 +1,9 @@ attributes['amount'] = strval(round($value, 2)); } + + public static function routeBinder($value) + { + if (Auth::check()) { + $object = LimitRepetition::where('limit_repetitions.id', $value) + ->leftjoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id') + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('budgets.user_id', Auth::user()->id) + ->first(['limit_repetitions.*']); + if ($object) { + return $object; + } + } + throw new NotFoundHttpException; + } + } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index d4e9e7c44b..b4bcab8036 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -1,10 +1,12 @@ attributes['targetamount'] = strval(round($value, 2)); } + + public static function routeBinder(PiggyBank $value) + { + if (Auth::check()) { + if ($value->account->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index d1bd328143..f9aa51a1a0 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -2,11 +2,13 @@ namespace FireflyIII\Models; +use Auth; use Carbon\Carbon; use Crypt; use FireflyIII\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; /** @@ -166,4 +168,17 @@ class Tag extends Model { return $this->belongsTo('FireflyIII\User'); } + + + public static function routeBinder(Tag $value) + { + if (Auth::check()) { + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + + } diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index bd9b6db6ad..168bfb9bf5 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -1,9 +1,11 @@ hasMany('FireflyIII\Models\TransactionJournal'); } + + /** + * @param TransactionCurrency $currency + */ + public static function routeBinder(TransactionCurrency $currency) + { + if (Auth::check()) { + return $currency; + } + throw new NotFoundHttpException; + } } diff --git a/app/Support/Domain.php b/app/Support/Domain.php index 198a08bbc6..7cdd39625c 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -28,7 +28,7 @@ class Domain 'bill' => 'FireflyIII\Models\Bill', 'budget' => 'FireflyIII\Models\Budget', 'category' => 'FireflyIII\Models\Category', - 'currency' => 'FireflyIII\Models\Currency', + 'currency' => 'FireflyIII\Models\TransactionCurrency', 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', 'piggyBank' => 'FireflyIII\Models\PiggyBank', 'tj' => 'FireflyIII\Models\TransactionJournal', From 2003d37a9a7cf29a506d9c36592586d7116e2c97 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 16:10:12 +0100 Subject: [PATCH 060/276] Fix for php 5.6 thing. --- app/Http/Middleware/Binder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Middleware/Binder.php b/app/Http/Middleware/Binder.php index 87fa2fe0ff..adb05d03df 100644 --- a/app/Http/Middleware/Binder.php +++ b/app/Http/Middleware/Binder.php @@ -48,6 +48,7 @@ class Binder */ private function performBinding($key, $value, $route) { - return $this->binders[$key]::routeBinder($value, $route); + $class = $this->binders[$key]; + return $class::routeBinder($value, $route); } } From a55e29190871012f641dee86b6cdd8cc9e4498e3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 18:02:36 +0100 Subject: [PATCH 061/276] Removed money_format. --- app/Support/Amount.php | 76 +++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/app/Support/Amount.php b/app/Support/Amount.php index bd0624f002..6298bc61f6 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -6,6 +6,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; +use NumberFormatter; use Preferences as Prefs; /** @@ -15,6 +16,38 @@ use Preferences as Prefs; */ class Amount { + + /** + * This method will properly format the given number, in color or "black and white", + * as a currency, given two things: the currency required and the current locale. + * + * @param TransactionCurrency $format + * @param $amount + * @param bool $coloured + * + * @return string + */ + public function formatAnything(TransactionCurrency $format, $amount, $coloured = true) + { + $locale = setlocale(LC_MONETARY, 0); + $a = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $result = $a->formatCurrency($amount, $format->code); + + if ($coloured === true) { + if ($amount == 0) { + return '' . $result . ''; + } + if ($amount > 0) { + return '' . $result . ''; + } + + return '' . $result . ''; + + } + + return $result; + } + /** * @param $amount * @param bool $coloured @@ -23,11 +56,7 @@ class Amount */ public function format($amount, $coloured = true) { - $currencySymbol = $this->getCurrencySymbol(); - - return $this->formatWithSymbol($currencySymbol, $amount, $coloured); - - + return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured); } /** @@ -58,22 +87,7 @@ class Amount */ public function formatWithSymbol($symbol, $amount, $coloured = true) { - $amount = floatval($amount); - $amount = round($amount, 2); - $string = money_format('%!.2n', $amount); - - if ($coloured === true) { - if ($amount === 0.0) { - return '' . $symbol . ' ' . $string . ''; - } - if ($amount > 0) { - return '' . $symbol . ' ' . $string . ''; - } - - return '' . $symbol . ' ' . $string . ''; - } - - return $symbol . ' ' . $string; + return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured); } /** @@ -93,27 +107,20 @@ class Amount return $cache->get(); // @codeCoverageIgnore } - - if (is_null($journal->symbol)) { - $symbol = $journal->transactionCurrency->symbol; - } else { - $symbol = $journal->symbol; - } - if ($journal->isTransfer() && $coloured) { - $txt = '' . $this->formatWithSymbol($symbol, $journal->amount_positive, false) . ''; + $txt = '' . $this->formatAnything($journal->transactionCurrency, $journal->amount_positive, false) . '';; $cache->store($txt); return $txt; } if ($journal->isTransfer() && !$coloured) { - $txt = $this->formatWithSymbol($symbol, $journal->amount_positive, false); + $txt = $this->formatAnything($journal->transactionCurrency, $journal->amount_positive, false); $cache->store($txt); return $txt; } - $txt = $this->formatWithSymbol($symbol, $journal->amount, $coloured); + $txt = $this->formatAnything($journal->transactionCurrency, $journal->amount, $coloured); $cache->store($txt); return $txt; @@ -127,10 +134,9 @@ class Amount */ public function formatTransaction(Transaction $transaction, $coloured = true) { - $symbol = $transaction->transactionJournal->transactionCurrency->symbol; - $amount = floatval($transaction->amount); + $currency = $transaction->transactionJournal->transactionCurrency; - return $this->formatWithSymbol($symbol, $amount, $coloured); + return $this->formatAnything($currency, $transaction->amount); } /** @@ -168,7 +174,7 @@ class Amount } /** - * @return mixed|static + * @return TransactionCurrency */ public function getDefaultCurrency() { From 2a08a25064824e8dbcd5551b034898da6d5c1e09 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 19:29:52 +0100 Subject: [PATCH 062/276] Fixed a translation. --- resources/lang/nl_NL/firefly.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 9d731d940b..622c937a7c 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -2,6 +2,7 @@ return [ // general stuff: + 'language_incomplete' => 'Deze taal is nog niet helemaal af', 'test' => 'Nederlands geselecteerd!', 'close' => 'Sluiten', 'pleaseHold' => 'Momentje...', @@ -219,7 +220,7 @@ return [ 'createBudget' => 'Maak nieuw budget', 'inactiveBudgets' => 'Inactieve budgetten', 'without_budget_between' => 'Transacties zonder budget tussen :start en :end', - 'budget_in_month' => ':naam in :maand', + 'budget_in_month' => ':name in :month', 'delete_budget' => 'Verwijder budget ":name"', 'edit_budget' => 'Wijzig budget ":name"', 'update_amount' => 'Bedrag bijwerken', From 2973765866bfe8158142febfc154bd6cc692dc19 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 19:37:21 +0100 Subject: [PATCH 063/276] New translations. --- resources/lang/en_US/firefly.php | 1 + resources/lang/nl_NL/firefly.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) mode change 100644 => 100755 resources/lang/nl_NL/firefly.php diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6a8cb5a3e1..6a4ad392f9 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -275,6 +275,7 @@ return [ 'category' => 'Category', 'delete_category' => 'Delete category ":name"', 'store_category' => 'Store new category', + 'without_category_between' => 'Without category between :start and :end', // transactions: 'update_withdrawal' => 'Update withdrawal', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php old mode 100644 new mode 100755 index 622c937a7c..0c6f1af9ee --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -220,7 +220,7 @@ return [ 'createBudget' => 'Maak nieuw budget', 'inactiveBudgets' => 'Inactieve budgetten', 'without_budget_between' => 'Transacties zonder budget tussen :start en :end', - 'budget_in_month' => ':name in :month', + 'budget_in_month' => ':naam in :maand', 'delete_budget' => 'Verwijder budget ":name"', 'edit_budget' => 'Wijzig budget ":name"', 'update_amount' => 'Bedrag bijwerken', From e0b2a6e627235d360b10f460fec72def09a1b274 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Jan 2016 19:39:38 +0100 Subject: [PATCH 064/276] Translations. --- resources/lang/nl_NL/firefly.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) mode change 100755 => 100644 resources/lang/nl_NL/firefly.php diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php old mode 100755 new mode 100644 index 0c6f1af9ee..c34aef8a41 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -220,7 +220,7 @@ return [ 'createBudget' => 'Maak nieuw budget', 'inactiveBudgets' => 'Inactieve budgetten', 'without_budget_between' => 'Transacties zonder budget tussen :start en :end', - 'budget_in_month' => ':naam in :maand', + 'budget_in_month' => ':name in :month', 'delete_budget' => 'Verwijder budget ":name"', 'edit_budget' => 'Wijzig budget ":name"', 'update_amount' => 'Bedrag bijwerken', @@ -275,6 +275,7 @@ return [ 'category' => 'Categorie', 'delete_category' => 'Verwijder categorie ":name"', 'store_category' => 'Sla nieuwe categorie op', + 'without_category_between' => 'Zonder categorie tussen :start en :end', // transactions: 'update_withdrawal' => 'Wijzig uitgave', From 19444551e47a319426776857cc7f402184f92989 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Jan 2016 12:16:25 +0100 Subject: [PATCH 065/276] Get withdrawals only. --- app/Repositories/Budget/BudgetRepository.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 2750dcfe6a..75c9ca247f 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -265,6 +265,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00')) ->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00')) ->first(['limit_repetitions.*']); + return $data; } @@ -402,6 +403,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn { return Auth::user() ->transactionjournals() + ->transactionTypes([TransactionType::WITHDRAWAL]) ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->whereNull('budget_transaction_journal.id') ->before($end) From b26164a168f843ab389e017293f51a7339973dea Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Jan 2016 12:16:34 +0100 Subject: [PATCH 066/276] More test data. --- database/seeds/TestDataSeeder.php | 112 +++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 9 deletions(-) diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 9d6bf19497..f4afe7afaf 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -8,6 +8,7 @@ use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Category; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankEvent; +use FireflyIII\Models\Preference; use FireflyIII\Models\Role; use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; @@ -48,8 +49,11 @@ class TestDataSeeder extends Seeder $this->createBills(); $this->createPiggybanks(); + // preference to only see account #1 on frontpage. + $this->createPreferences(); + // dates: - $start = Carbon::now()->subYears(5)->startOfMonth(); + $start = Carbon::now()->subYears(2)->startOfMonth(); $end = Carbon::now()->endOfDay(); @@ -57,7 +61,7 @@ class TestDataSeeder extends Seeder while ($current < $end) { $month = $current->format('F Y'); // create salaries: - $this->createIncome('Salary ' . $month, $current, rand(2000, 2200)); + $this->createIncome('Salary ' . $month, $current, rand(2000, 2100)); // pay bills: $this->createRent('Rent for ' . $month, $current, 800); @@ -65,6 +69,7 @@ class TestDataSeeder extends Seeder $this->createTV('TV bill for ' . $month, $current, 60); $this->createPower('Power bill for ' . $month, $current, 120); + // pay daily groceries: $this->createGroceries($current); @@ -77,9 +82,13 @@ class TestDataSeeder extends Seeder // save money every month: $this->createSavings($current); + // buy gas for the car every month: + $this->createCar($current); + // budget limit for this month, on "Groceries". $this->createBudgetLimit($current, 'Groceries', 400); $this->createBudgetLimit($current, 'Bills', 1000); + $this->createBudgetLimit($current, 'Car', 100); echo 'Created test data for ' . $month . "\n"; $current->addMonth(); @@ -167,7 +176,7 @@ class TestDataSeeder extends Seeder protected function createExpenseAccounts() { $expenses = ['Adobe', 'Google', 'Vitens', 'Albert Heijn', 'PLUS', 'Apple', 'Bakker', 'Belastingdienst', 'bol.com', 'Cafe Central', 'conrad.nl', - 'coolblue', + 'coolblue', 'Shell', 'DUO', 'Etos', 'FEBO', 'Greenchoice', 'Halfords', 'XS4All', 'iCentre', 'Jumper', 'Land lord']; foreach ($expenses as $name) { // create account: @@ -638,15 +647,16 @@ class TestDataSeeder extends Seeder $start->startOfMonth(); $end->endOfMonth(); - $fromAccount = $this->findAccount('MyBank Checking Account'); - $stores = ['Albert Heijn', 'PLUS', 'Bakker']; - $category = Category::firstOrCreateEncrypted(['name' => 'Daily groceries', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]); + $fromAccount = $this->findAccount('MyBank Checking Account'); + $stores = ['Albert Heijn', 'PLUS', 'Bakker']; + $descriptions = ['Groceries', 'Bought some food', 'Got groceries']; + $category = Category::firstOrCreateEncrypted(['name' => 'Daily groceries', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]); $current = clone $start; while ($current < $end && $current < $today) { // daily groceries: - $amount = rand(1000, 2500) / 100; + $amount = rand(1500, 2500) / 100; $toAccount = $this->findAccount($stores[rand(0, count($stores) - 1)]); $journal = TransactionJournal::create( @@ -654,7 +664,7 @@ class TestDataSeeder extends Seeder 'user_id' => $this->user->id, 'transaction_type_id' => 1, 'transaction_currency_id' => 1, - 'description' => 'Groceries', + 'description' => $descriptions[rand(0, count($descriptions) - 1)], 'completed' => 1, 'date' => $current, ] @@ -903,5 +913,89 @@ class TestDataSeeder extends Seeder return null; } + protected function createCar($date) + { + // twice: + $date = new Carbon($date->format('Y-m') . '-10'); // paid on 10th + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('Shell'); + $category = Category::firstOrCreateEncrypted(['name' => 'Car', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Car', 'user_id' => $this->user->id]); + $amount = rand(4000, 5000) / 100; + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => 'Bought gas', + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + // and again! + $date = new Carbon($date->format('Y-m') . '-20'); // paid on 20th + $amount = rand(4000, 5000) / 100; + + + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => 'Gas for car', + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + + // and again! + + return $journal; + } + + protected function createPreferences() + { + $preference = new Preference; + $preference->name = 'frontPageAccounts'; + $preference->data = [1]; + $preference->user()->associate($this->user); + $preference->save(); + } + } From 71bb88529a67d9be7d2ffc435ec99f2aa48baea2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Jan 2016 14:42:51 +0100 Subject: [PATCH 067/276] Also run php 7 tests. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index abb00b138e..861a66a8b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ sudo: false php: - 5.6 + - 7 install: - composer install From 5a7607f6c66b09071955e2df6cf4e1c7ed27edcf Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Jan 2016 18:02:24 +0100 Subject: [PATCH 068/276] More consistent monetary formatting. --- app/Http/Controllers/Controller.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 813c1e88b1..2d33717042 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -9,6 +9,7 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; +use NumberFormatter; use Preferences; use View; @@ -21,10 +22,10 @@ class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; - /** @var string|\Symfony\Component\Translation\TranslatorInterface */ + /** @var string|\Symfony\Component\Translation\TranslatorInterface */ protected $monthFormat; - /** @var string|\Symfony\Component\Translation\TranslatorInterface */ + /** @var string|\Symfony\Component\Translation\TranslatorInterface */ protected $monthAndDayFormat; /** @@ -51,10 +52,17 @@ class Controller extends BaseController setlocale(LC_TIME, $locale); setlocale(LC_MONETARY, $locale); + // change localeconv to a new array: + $numberFormatter = numfmt_create($lang, NumberFormatter::CURRENCY); + $localeconv = [ + 'mon_decimal_point' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL)), + 'mon_thousands_sep' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL)), + 'frac_digits' => $numberFormatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS) + ]; View::share('monthFormat', $this->monthFormat); View::share('monthAndDayFormat', $this->monthAndDayFormat); View::share('language', $lang); - View::share('localeconv', localeconv()); + View::share('localeconv', $localeconv); } } From c96eb8753ebf1922b7979aa793c996225f89c45b Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 20:41:43 +0100 Subject: [PATCH 069/276] New bread crumb for rules controller. --- app/Http/breadcrumbs.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index cf3de22751..6ae979f176 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -361,6 +361,16 @@ Breadcrumbs::register( } ); +/** + * Rules + */ +Breadcrumbs::register( + 'rules.index', function (BreadCrumbGenerator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push(trans('firefly.rules'), route('rules.index')); +} +); + // search Breadcrumbs::register( 'search', function (BreadCrumbGenerator $breadcrumbs, $query) { From d889094863a9c82a9fd35a59f0bc4d7246cd7a9e Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 20:41:50 +0100 Subject: [PATCH 070/276] New route for rules index. --- app/Http/routes.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/routes.php b/app/Http/routes.php index 01fb4e9160..8b164a8ec8 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -242,6 +242,11 @@ Route::group( Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); Route::get('/reports/report/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']); + /** + * Rules Controller + */ + Route::get('/rules', ['uses' => 'RuleController@index', 'as' => 'rules.index']); + /** * Search Controller */ From 70ed4188b6f7e9d283998ad4239641cf26f04a67 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 20:41:57 +0100 Subject: [PATCH 071/276] New controller for rules. --- app/Http/Controllers/RuleController.php | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 app/Http/Controllers/RuleController.php diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php new file mode 100644 index 0000000000..99dc3b75f3 --- /dev/null +++ b/app/Http/Controllers/RuleController.php @@ -0,0 +1,34 @@ + Date: Mon, 11 Jan 2016 20:42:08 +0100 Subject: [PATCH 072/276] New migration (empty) --- .../2016_01_11_193428_changes_for_v370.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 database/migrations/2016_01_11_193428_changes_for_v370.php diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php new file mode 100644 index 0000000000..1ca5e7e66f --- /dev/null +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -0,0 +1,36 @@ + Date: Mon, 11 Jan 2016 20:42:15 +0100 Subject: [PATCH 073/276] New translation --- resources/lang/en_US/firefly.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6a4ad392f9..ecdbe1b167 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -38,6 +38,9 @@ return [ 'new_budget' => 'New budget', 'new_bill' => 'New bill', + // rules + 'rules' => 'Rules', + // tags 'store_new_tag' => 'Store new tag', 'update_tag' => 'Update tag', From bdec94ead63e06b7bfd028f75ba1bd45e52a40a9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 20:42:20 +0100 Subject: [PATCH 074/276] Sidebar entry --- resources/views/partials/menu-sidebar.twig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/views/partials/menu-sidebar.twig b/resources/views/partials/menu-sidebar.twig index 3c900ab06d..1683986cbc 100644 --- a/resources/views/partials/menu-sidebar.twig +++ b/resources/views/partials/menu-sidebar.twig @@ -86,7 +86,7 @@ -
  • +
  • {{ 'moneyManagement'|_ }} @@ -101,6 +101,10 @@ {{ 'bills'|_ }}
  • +
  • + + {{ 'rules'|_ }} +
  • From c38a4a15ff6ad49bcd194052b2cece3abe4d4e1d Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 20:42:24 +0100 Subject: [PATCH 075/276] View --- resources/views/rules/index.twig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 resources/views/rules/index.twig diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig new file mode 100644 index 0000000000..9a934d37b3 --- /dev/null +++ b/resources/views/rules/index.twig @@ -0,0 +1,20 @@ +{% extends "./layout/default.twig" %} +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} +{% block content %} + +
    +
    +
    +
    +

    {{ 'rules'|_ }}

    +
    +
    + Bla bla bla +
    +
    +
    +
    + +{% endblock %} From c1346d4c86f4df763983ec5e3f1184746ef7e00c Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 21:10:11 +0100 Subject: [PATCH 076/276] Migration for rules. --- .../2016_01_11_193428_changes_for_v370.php | 102 +++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index 1ca5e7e66f..1a10706457 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -8,6 +8,7 @@ */ use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV370 @@ -21,7 +22,100 @@ class ChangesForV370 extends Migration */ public function up() { - // + // new table "rule_groups" + Schema::create( + 'rule_groups', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id')->unsigned(); + $table->unsignedSmallInteger('order'); + $table->string('title', 2048); + $table->mediumText('description'); + $table->unsignedTinyInteger('active')->default(1); + + // connect rule groups to users + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + // order must be unique for rule group: + $table->unique(['user_id', 'order']); + } + ); + + + // new table "rules": + Schema::create( + 'rules', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id')->unsigned(); + $table->integer('rule_group_id')->unsigned(); + $table->unsignedSmallInteger('order'); + $table->string('title', 2048); + $table->mediumText('description'); + $table->unsignedTinyInteger('active')->default(1); + $table->unsignedTinyInteger('stop_processing')->default(0); + + // connect rules to users + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + // connect rules to rule groups + $table->foreign('rule_group_id')->references('id')->on('rule_groups')->onDelete('cascade'); + + // order must be unique for rules: + $table->unique(['user_id', 'order']); + } + ); + + + // new table "rule_triggers" + Schema::create( + 'rule_triggers', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('rule_id')->unsigned(); + $table->unsignedSmallInteger('order'); + $table->string('title', 2048); + + $table->string('trigger_field', 2048); + $table->string('trigger_type', 1024); + $table->string('trigger_value', 2048); + + $table->unsignedTinyInteger('active')->default(1); + $table->unsignedTinyInteger('stop_processing')->default(0); + + // order must be unique for rule triggers: + $table->unique(['rule_id', 'order']); + + // connect rule tiggers to rules + $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); + } + ); + + // new table "rule_actions" + Schema::create( + 'rule_actions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('rule_id')->unsigned(); + $table->unsignedSmallInteger('order'); + + $table->unsignedTinyInteger('active')->default(1); + $table->unsignedTinyInteger('stop_processing')->default(0); + + $table->string('action_field', 2048); + $table->string('action', 1024); + $table->string('action_value', 2048); + + // connect rule actions to rules + $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); + + // order must be unique for rule triggers: + $table->unique(['rule_id', 'order']); + } + ); + } /** @@ -31,6 +125,10 @@ class ChangesForV370 extends Migration */ public function down() { - // + Schema::drop('rule_actions'); + Schema::drop('rule_triggers'); + Schema::drop('rules'); + Schema::drop('rule_groups'); + } } From 5da5024ad3f1f13e499c4f211c5d6b6d3af5e63a Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 21:28:29 +0100 Subject: [PATCH 077/276] New models. --- app/Models/Rule.php | 54 ++++++++++++++++++++++++++++++++++++++ app/Models/RuleAction.php | 28 ++++++++++++++++++++ app/Models/RuleGroup.php | 37 ++++++++++++++++++++++++++ app/Models/RuleTrigger.php | 28 ++++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 app/Models/Rule.php create mode 100644 app/Models/RuleAction.php create mode 100644 app/Models/RuleGroup.php create mode 100644 app/Models/RuleTrigger.php diff --git a/app/Models/Rule.php b/app/Models/Rule.php new file mode 100644 index 0000000000..ffa7957e40 --- /dev/null +++ b/app/Models/Rule.php @@ -0,0 +1,54 @@ +belongsTo('FireflyIII\User'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function ruleGroup() + { + return $this->belongsTo('FireflyIII\Models\RuleGroup'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function ruleActions() + { + return $this->hasMany('FireflyIII\Models\RuleAction'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function ruleTriggers() + { + return $this->hasMany('FireflyIII\Models\RuleTrigger'); + } + + +} diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php new file mode 100644 index 0000000000..fcd728bf94 --- /dev/null +++ b/app/Models/RuleAction.php @@ -0,0 +1,28 @@ +belongsTo('FireflyIII\Models\Rule'); + } +} diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php new file mode 100644 index 0000000000..b82807e741 --- /dev/null +++ b/app/Models/RuleGroup.php @@ -0,0 +1,37 @@ +belongsTo('FireflyIII\User'); + } + + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function rules() + { + return $this->hasMany('FireflyIII\Models\Rule'); + } +} diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php new file mode 100644 index 0000000000..9bef919950 --- /dev/null +++ b/app/Models/RuleTrigger.php @@ -0,0 +1,28 @@ +belongsTo('FireflyIII\Models\Rule'); + } +} From da7eb615dbce78465c36e847a93f0cf9438b36ec Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Jan 2016 21:42:51 +0100 Subject: [PATCH 078/276] Added php doc. --- app/Models/Account.php | 1 - app/Models/PiggyBank.php | 1 + app/Models/Rule.php | 15 +++++++++++++++ app/Models/RuleAction.php | 11 +++++++++++ app/Models/RuleGroup.php | 11 +++++++++++ app/Models/RuleTrigger.php | 12 ++++++++++++ app/User.php | 18 ++++++++++++++++++ 7 files changed, 68 insertions(+), 1 deletion(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index ff22ff5fdb..d381f6b4c1 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -34,7 +34,6 @@ use Watson\Validating\ValidatingTrait; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account hasMetaValue($name, $value) * @property string $startBalance * @property string $endBalance - * */ class Account extends Model { diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index b4bcab8036..c1c7513934 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -27,6 +27,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read Account $account * @property-read Collection|PiggyBankRepetition[] $piggyBankRepetitions * @property-read Collection|PiggyBankEvent[] $piggyBankEvents + * @property string $reminder */ class PiggyBank extends Model { diff --git a/app/Models/Rule.php b/app/Models/Rule.php index ffa7957e40..dc08fd18fd 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -15,6 +15,21 @@ use Illuminate\Database\Eloquent\Model; * Class Rule * * @package FireflyIII\Models + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property integer $user_id + * @property integer $rule_group_id + * @property integer $order + * @property string $title + * @property string $description + * @property boolean $active + * @property boolean $stop_processing + * @property-read \FireflyIII\User $user + * @property-read \FireflyIII\Models\RuleGroup $ruleGroup + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleAction[] $ruleActions + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleTrigger[] $ruleTriggers */ class Rule extends Model { diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index fcd728bf94..4ef20d4a1d 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -15,6 +15,17 @@ use Illuminate\Database\Eloquent\Model; * Class RuleAction * * @package FireflyIII\Models + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property integer $rule_id + * @property integer $order + * @property boolean $active + * @property boolean $stop_processing + * @property string $action_field + * @property string $action + * @property string $action_value + * @property-read \FireflyIII\Models\Rule $rule */ class RuleAction extends Model { diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index b82807e741..894a88e748 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -15,6 +15,17 @@ use Illuminate\Database\Eloquent\Model; * Class RuleGroup * * @package FireflyIII\Models + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property integer $user_id + * @property integer $order + * @property string $title + * @property string $description + * @property boolean $active + * @property-read \FireflyIII\User $user + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Rule[] $rules */ class RuleGroup extends Model { diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 9bef919950..ccf52f1243 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -15,6 +15,18 @@ use Illuminate\Database\Eloquent\Model; * Class RuleTrigger * * @package FireflyIII\Models + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property integer $rule_id + * @property integer $order + * @property string $title + * @property string $trigger_field + * @property string $trigger_type + * @property string $trigger_value + * @property boolean $active + * @property boolean $stop_processing + * @property-read \FireflyIII\Models\Rule $rule */ class RuleTrigger extends Model { diff --git a/app/User.php b/app/User.php index e8b52afc07..da3a79a52b 100755 --- a/app/User.php +++ b/app/User.php @@ -9,6 +9,24 @@ use Zizaco\Entrust\Traits\EntrustUserTrait; * Class User * * @package FireflyIII + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $email + * @property string $password + * @property string $remember_token + * @property string $reset + * @property boolean $blocked + * @property string $blocked_code + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Account[] $accounts + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Tag[] $tags + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Bill[] $bills + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Preference[] $preferences + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionjournals + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Role[] $roles */ class User extends Authenticatable { From 06d11c9133fc630487b5b393bca58823af504e83 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 20:36:47 +0100 Subject: [PATCH 079/276] New stuff for the rule thing. --- app/Models/Rule.php | 30 ++++++----- app/Models/RuleTrigger.php | 4 +- config/firefly.php | 45 +++++++++++++++- .../2016_01_11_193428_changes_for_v370.php | 21 ++++---- database/seeds/TestDataSeeder.php | 53 ++++++++++++++++++- 5 files changed, 123 insertions(+), 30 deletions(-) diff --git a/app/Models/Rule.php b/app/Models/Rule.php index dc08fd18fd..c4c73aac1c 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -9,30 +9,32 @@ namespace FireflyIII\Models; +use Crypt; use Illuminate\Database\Eloquent\Model; /** * Class Rule * * @package FireflyIII\Models - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $user_id - * @property integer $rule_group_id - * @property integer $order - * @property string $title - * @property string $description - * @property boolean $active - * @property boolean $stop_processing - * @property-read \FireflyIII\User $user - * @property-read \FireflyIII\Models\RuleGroup $ruleGroup - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleAction[] $ruleActions + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property integer $user_id + * @property integer $rule_group_id + * @property integer $order + * @property string $title + * @property string $description + * @property boolean $active + * @property boolean $stop_processing + * @property-read \FireflyIII\User $user + * @property-read \FireflyIII\Models\RuleGroup $ruleGroup + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleAction[] $ruleActions * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleTrigger[] $ruleTriggers */ class Rule extends Model { + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index ccf52f1243..daaeea0e98 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -12,16 +12,14 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; /** - * Class RuleTrigger + * FireflyIII\Models\RuleTrigger * - * @package FireflyIII\Models * @property integer $id * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property integer $rule_id * @property integer $order * @property string $title - * @property string $trigger_field * @property string $trigger_type * @property string $trigger_value * @property boolean $active diff --git a/config/firefly.php b/config/firefly.php index 3659db04e6..5439b21e70 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -98,7 +98,7 @@ return [ 'Revenue account' => 'revenue', 'Cash account' => 'cash', ], - 'languages' => [ + 'languages' => [ 'en_US' => ['name_locale' => 'English', 'name_english' => 'English', 'complete' => true], 'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true], 'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portugese (Brazil)', 'complete' => false], @@ -147,4 +147,47 @@ return [ 'pt_BR' => '%B %e, %Y', ], + 'bindables' => [ + // models + 'account' => 'FireflyIII\Models\Account', + 'attachment' => 'FireflyIII\Models\Attachment', + 'bill' => 'FireflyIII\Models\Bill', + 'budget' => 'FireflyIII\Models\Budget', + 'category' => 'FireflyIII\Models\Category', + 'currency' => 'FireflyIII\Models\TransactionCurrency', + 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', + 'piggyBank' => 'FireflyIII\Models\PiggyBank', + 'tj' => 'FireflyIII\Models\TransactionJournal', + 'tag' => 'FireflyIII\Models\Tag', + // lists + 'accountList' => 'FireflyIII\Support\Binder\AccountList', + 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', + 'categoryList' => 'FireflyIII\Support\Binder\CategoryList', + + // others + 'start_date' => 'FireflyIII\Support\Binder\Date', + 'end_date' => 'FireflyIII\Support\Binder\Date' + ], + 'rule-triggers' => [ + 'from_account_starts' => 'FireflyIII\Rules\Triggers', + 'from_account_ends' => 'FireflyIII\Rules\Triggers', + 'from_account_is' => 'FireflyIII\Rules\Triggers', + 'from_account_contains' => 'FireflyIII\Rules\Triggers', + 'to_account_starts' => 'FireflyIII\Rules\Triggers', + 'to_account_ends' => 'FireflyIII\Rules\Triggers', + 'to_account_is' => 'FireflyIII\Rules\Triggers', + 'to_account_contains' => 'FireflyIII\Rules\Triggers', + + + 'transaction_type' => 'FireflyIII\Rules\Triggers', + 'amount_less' => 'FireflyIII\Rules\Triggers', + 'amount_exactly' => 'FireflyIII\Rules\Triggers', + 'amount_exactly_not' => 'FireflyIII\Rules\Triggers', + 'amount_more' => 'FireflyIII\Rules\Triggers', + 'description_starts' => 'FireflyIII\Rules\Triggers', + 'description_ends' => 'FireflyIII\Rules\Triggers', + 'description_contains' => 'FireflyIII\Rules\Triggers', + 'description_is' => 'FireflyIII\Rules\Triggers', + ], + ]; diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index 1a10706457..542263f947 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -30,8 +30,8 @@ class ChangesForV370 extends Migration $table->softDeletes(); $table->integer('user_id')->unsigned(); $table->unsignedSmallInteger('order'); - $table->string('title', 2048); - $table->mediumText('description'); + $table->string('title', 255); + $table->text('description'); $table->unsignedTinyInteger('active')->default(1); // connect rule groups to users @@ -52,8 +52,8 @@ class ChangesForV370 extends Migration $table->integer('user_id')->unsigned(); $table->integer('rule_group_id')->unsigned(); $table->unsignedSmallInteger('order'); - $table->string('title', 2048); - $table->mediumText('description'); + $table->string('title', 255); + $table->text('description'); $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('stop_processing')->default(0); @@ -78,9 +78,8 @@ class ChangesForV370 extends Migration $table->unsignedSmallInteger('order'); $table->string('title', 2048); - $table->string('trigger_field', 2048); - $table->string('trigger_type', 1024); - $table->string('trigger_value', 2048); + $table->string('trigger_type', 50); + $table->string('trigger_value', 255); $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('stop_processing')->default(0); @@ -88,7 +87,7 @@ class ChangesForV370 extends Migration // order must be unique for rule triggers: $table->unique(['rule_id', 'order']); - // connect rule tiggers to rules + // connect rule triggers to rules $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); } ); @@ -104,9 +103,9 @@ class ChangesForV370 extends Migration $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('stop_processing')->default(0); - $table->string('action_field', 2048); - $table->string('action', 1024); - $table->string('action_value', 2048); + $table->string('action_field', 50); + $table->string('action', 50); + $table->string('action_value', 255); // connect rule actions to rules $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index f4afe7afaf..26498f51a9 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -10,6 +10,9 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Preference; use FireflyIII\Models\Role; +use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -33,6 +36,52 @@ class TestDataSeeder extends Seeder public function __construct() { + } + + public function createRules() + { + // basic rules group. user should always have one + $ruleGroup = new RuleGroup; + $ruleGroup->user()->associate($this->user); + $ruleGroup->order = 1; + $ruleGroup->title = 'Default rules'; + $ruleGroup->description = 'All your rules not in a particular group.'; + $ruleGroup->active = 1; + $ruleGroup->save(); + + + // a normal rule: saves transactions where description contains "groceries" + // and from account is "MyBank Checking Account" + // send it to Groceries/Groceries + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate($ruleGroup); + $rule->order = 1; + $rule->title = 'A strange rule for testing.'; + $rule->description = 'This rule triggers on transactions with the description "David Bowie" and puts them in the category "Blackstar".'; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->save(); + + // trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->title = 'Groceries'; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'groceries'; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + + // actions for this rule. + + // TODO a rule that triggers on something, just like the previous one, but it has "stop_processing" set to 1. + // TODO a rule that triggers on that same thing, but it will not file, because the previous rule made FF skip it. + + // TODO rule with specific actions. + // TODO rule with actions and one action has stop_processing and other actions are not processed. Somewhere in test? + + } /** @@ -49,6 +98,8 @@ class TestDataSeeder extends Seeder $this->createBills(); $this->createPiggybanks(); + $this->createRules(); + // preference to only see account #1 on frontpage. $this->createPreferences(); @@ -649,7 +700,7 @@ class TestDataSeeder extends Seeder $fromAccount = $this->findAccount('MyBank Checking Account'); $stores = ['Albert Heijn', 'PLUS', 'Bakker']; - $descriptions = ['Groceries', 'Bought some food', 'Got groceries']; + $descriptions = ['Groceries', 'Bought some groceries', 'Got groceries']; $category = Category::firstOrCreateEncrypted(['name' => 'Daily groceries', 'user_id' => $this->user->id]); $budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]); From 35f493beff2ed61689c4385452a7f4ea29f36a70 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 20:37:47 +0100 Subject: [PATCH 080/276] Move domain stuff to configuration. --- app/Support/Domain.php | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/app/Support/Domain.php b/app/Support/Domain.php index 7cdd39625c..53b9012298 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -9,6 +9,8 @@ namespace FireflyIII\Support; +use Config; + /** * Class Domain * @@ -21,29 +23,7 @@ class Domain */ public static function getBindables() { - return [ - // models - 'account' => 'FireflyIII\Models\Account', - 'attachment' => 'FireflyIII\Models\Attachment', - 'bill' => 'FireflyIII\Models\Bill', - 'budget' => 'FireflyIII\Models\Budget', - 'category' => 'FireflyIII\Models\Category', - 'currency' => 'FireflyIII\Models\TransactionCurrency', - 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', - 'piggyBank' => 'FireflyIII\Models\PiggyBank', - 'tj' => 'FireflyIII\Models\TransactionJournal', - 'tag' => 'FireflyIII\Models\Tag', - // lists - 'accountList' => 'FireflyIII\Support\Binder\AccountList', - 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', - 'categoryList' => 'FireflyIII\Support\Binder\CategoryList', - - // others - 'start_date' => 'FireflyIII\Support\Binder\Date', - 'end_date' => 'FireflyIII\Support\Binder\Date' - ]; - + return Config::get('firefly.bindables'); } - } \ No newline at end of file From 74b538805b050c5a56bc736183fd7044b7bcfe2c Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 20:38:19 +0100 Subject: [PATCH 081/276] Expand config. --- config/firefly.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/config/firefly.php b/config/firefly.php index 3659db04e6..694d6d762f 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -98,7 +98,7 @@ return [ 'Revenue account' => 'revenue', 'Cash account' => 'cash', ], - 'languages' => [ + 'languages' => [ 'en_US' => ['name_locale' => 'English', 'name_english' => 'English', 'complete' => true], 'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true], 'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portugese (Brazil)', 'complete' => false], @@ -146,5 +146,27 @@ return [ 'fr_FR' => '%B %e, %Y', 'pt_BR' => '%B %e, %Y', ], + 'bindables' => [ + // models + 'account' => 'FireflyIII\Models\Account', + 'attachment' => 'FireflyIII\Models\Attachment', + 'bill' => 'FireflyIII\Models\Bill', + 'budget' => 'FireflyIII\Models\Budget', + 'category' => 'FireflyIII\Models\Category', + 'currency' => 'FireflyIII\Models\TransactionCurrency', + 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', + 'piggyBank' => 'FireflyIII\Models\PiggyBank', + 'tj' => 'FireflyIII\Models\TransactionJournal', + 'tag' => 'FireflyIII\Models\Tag', + // lists + 'accountList' => 'FireflyIII\Support\Binder\AccountList', + 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', + 'categoryList' => 'FireflyIII\Support\Binder\CategoryList', + + // others + 'start_date' => 'FireflyIII\Support\Binder\Date', + 'end_date' => 'FireflyIII\Support\Binder\Date' + ], + ]; From f9703eca4c107c36d8082313a98d206a8da12e6e Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 20:56:36 +0100 Subject: [PATCH 082/276] Removed double configuration. --- config/firefly.php | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/config/firefly.php b/config/firefly.php index b77492283b..d468bad89f 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -168,28 +168,6 @@ return [ 'end_date' => 'FireflyIII\Support\Binder\Date' ], - - 'bindables' => [ - // models - 'account' => 'FireflyIII\Models\Account', - 'attachment' => 'FireflyIII\Models\Attachment', - 'bill' => 'FireflyIII\Models\Bill', - 'budget' => 'FireflyIII\Models\Budget', - 'category' => 'FireflyIII\Models\Category', - 'currency' => 'FireflyIII\Models\TransactionCurrency', - 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', - 'piggyBank' => 'FireflyIII\Models\PiggyBank', - 'tj' => 'FireflyIII\Models\TransactionJournal', - 'tag' => 'FireflyIII\Models\Tag', - // lists - 'accountList' => 'FireflyIII\Support\Binder\AccountList', - 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', - 'categoryList' => 'FireflyIII\Support\Binder\CategoryList', - - // others - 'start_date' => 'FireflyIII\Support\Binder\Date', - 'end_date' => 'FireflyIII\Support\Binder\Date' - ], 'rule-triggers' => [ 'from_account_starts' => 'FireflyIII\Rules\Triggers', 'from_account_ends' => 'FireflyIII\Rules\Triggers', From 44e76f951870e18b4869f82434367a9bd963cf03 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 20:56:43 +0100 Subject: [PATCH 083/276] Cleanup rule migration. --- .../2016_01_11_193428_changes_for_v370.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index 542263f947..776113c508 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -52,11 +52,13 @@ class ChangesForV370 extends Migration $table->integer('user_id')->unsigned(); $table->integer('rule_group_id')->unsigned(); $table->unsignedSmallInteger('order'); - $table->string('title', 255); - $table->text('description'); $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('stop_processing')->default(0); + $table->string('title', 255); + $table->text('description'); + + // connect rules to users $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -76,13 +78,13 @@ class ChangesForV370 extends Migration $table->timestamps(); $table->integer('rule_id')->unsigned(); $table->unsignedSmallInteger('order'); - $table->string('title', 2048); + $table->unsignedTinyInteger('active')->default(1); + $table->unsignedTinyInteger('stop_processing')->default(0); $table->string('trigger_type', 50); $table->string('trigger_value', 255); - $table->unsignedTinyInteger('active')->default(1); - $table->unsignedTinyInteger('stop_processing')->default(0); + // order must be unique for rule triggers: $table->unique(['rule_id', 'order']); @@ -99,7 +101,6 @@ class ChangesForV370 extends Migration $table->timestamps(); $table->integer('rule_id')->unsigned(); $table->unsignedSmallInteger('order'); - $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('stop_processing')->default(0); @@ -107,6 +108,10 @@ class ChangesForV370 extends Migration $table->string('action', 50); $table->string('action_value', 255); + + + + // connect rule actions to rules $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); From d644403d8a90ee50a992db70b284fb745bfd203c Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 20:56:53 +0100 Subject: [PATCH 084/276] Add php doc to rule controller. --- app/Http/Controllers/RuleController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 99dc3b75f3..d99ab1f4f6 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -19,6 +19,9 @@ use View; */ class RuleController extends Controller { + /** + * RuleController constructor. + */ public function __construct() { parent::__construct(); @@ -27,6 +30,9 @@ class RuleController extends Controller } + /** + * @return View + */ public function index() { return view('rules.index'); From 321890992eb8f37366b98db4144a5cec4e4ac0d6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:09:19 +0100 Subject: [PATCH 085/276] Cleanup php doc. --- app/Models/RuleAction.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 4ef20d4a1d..257ae41f7d 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -11,10 +11,10 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; + /** - * Class RuleAction + * FireflyIII\Models\RuleAction * - * @package FireflyIII\Models * @property integer $id * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at @@ -22,8 +22,7 @@ use Illuminate\Database\Eloquent\Model; * @property integer $order * @property boolean $active * @property boolean $stop_processing - * @property string $action_field - * @property string $action + * @property string $action_type * @property string $action_value * @property-read \FireflyIII\Models\Rule $rule */ From 166d32f073ef6a35627bc8fbd19ee667380bda84 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:09:28 +0100 Subject: [PATCH 086/276] Add actions --- config/firefly.php | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/config/firefly.php b/config/firefly.php index d468bad89f..2dbbae118b 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -177,17 +177,27 @@ return [ 'to_account_ends' => 'FireflyIII\Rules\Triggers', 'to_account_is' => 'FireflyIII\Rules\Triggers', 'to_account_contains' => 'FireflyIII\Rules\Triggers', - - - 'transaction_type' => 'FireflyIII\Rules\Triggers', - 'amount_less' => 'FireflyIII\Rules\Triggers', - 'amount_exactly' => 'FireflyIII\Rules\Triggers', - 'amount_exactly_not' => 'FireflyIII\Rules\Triggers', - 'amount_more' => 'FireflyIII\Rules\Triggers', - 'description_starts' => 'FireflyIII\Rules\Triggers', - 'description_ends' => 'FireflyIII\Rules\Triggers', - 'description_contains' => 'FireflyIII\Rules\Triggers', - 'description_is' => 'FireflyIII\Rules\Triggers', + 'transaction_type' => 'FireflyIII\Rules\Triggers', + 'amount_less' => 'FireflyIII\Rules\Triggers', + 'amount_exactly' => 'FireflyIII\Rules\Triggers', + 'amount_exactly_not' => 'FireflyIII\Rules\Triggers', + 'amount_more' => 'FireflyIII\Rules\Triggers', + 'description_starts' => 'FireflyIII\Rules\Triggers', + 'description_ends' => 'FireflyIII\Rules\Triggers', + 'description_contains' => 'FireflyIII\Rules\Triggers', + 'description_is' => 'FireflyIII\Rules\Triggers', + ], + 'rule-actions' => [ + 'set_category' => 'FireflyIII\Rules\Actions', + 'clear_category' => 'FireflyIII\Rules\Actions', + 'set_budget' => 'FireflyIII\Rules\Actions', + 'clear_budget' => 'FireflyIII\Rules\Actions', + 'add_tag' => 'FireflyIII\Rules\Actions', + 'remove_tag' => 'FireflyIII\Rules\Actions', + 'remove_all_tags' => 'FireflyIII\Rules\Actions', + 'set_description' => 'FireflyIII\Rules\Actions', + 'append_description' => 'FireflyIII\Rules\Actions', + 'prepend_description' => 'FireflyIII\Rules\Actions', ], ]; From d039bb0e036077ed35a6bb014f70bac2aae8231c Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:09:38 +0100 Subject: [PATCH 087/276] Update database. --- database/migrations/2016_01_11_193428_changes_for_v370.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index 776113c508..adb0833a9f 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -104,8 +104,7 @@ class ChangesForV370 extends Migration $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('stop_processing')->default(0); - $table->string('action_field', 50); - $table->string('action', 50); + $table->string('action_type', 50); $table->string('action_value', 255); From 1e54366f1ffa0e462c78f65404f64af90ad35491 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:09:51 +0100 Subject: [PATCH 088/276] Update test data. --- database/seeds/TestDataSeeder.php | 51 ++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 26498f51a9..4259fabf7d 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -11,6 +11,7 @@ use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Preference; use FireflyIII\Models\Role; use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\Tag; @@ -44,9 +45,9 @@ class TestDataSeeder extends Seeder $ruleGroup = new RuleGroup; $ruleGroup->user()->associate($this->user); $ruleGroup->order = 1; + $ruleGroup->active = 1; $ruleGroup->title = 'Default rules'; $ruleGroup->description = 'All your rules not in a particular group.'; - $ruleGroup->active = 1; $ruleGroup->save(); @@ -57,23 +58,59 @@ class TestDataSeeder extends Seeder $rule->user()->associate($this->user); $rule->ruleGroup()->associate($ruleGroup); $rule->order = 1; - $rule->title = 'A strange rule for testing.'; - $rule->description = 'This rule triggers on transactions with the description "David Bowie" and puts them in the category "Blackstar".'; $rule->active = 1; $rule->stop_processing = 0; + $rule->title = 'A strange rule for testing.'; + $rule->description = 'This rule triggers on transactions with the description "David Bowie" and puts them in the category "Blackstar".'; + $rule->save(); // trigger for this rule: $ruleTrigger = new RuleTrigger; $ruleTrigger->rule()->associate($rule); $ruleTrigger->order = 1; - $ruleTrigger->title = 'Groceries'; - $ruleTrigger->trigger_type = 'description_contains'; - $ruleTrigger->trigger_value = 'groceries'; $ruleTrigger->active = 1; $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'groceries'; - // actions for this rule. + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'Automated Groceries'; + $ruleAction->save(); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Groceries'; + $ruleAction->save(); + + + //$ruleAction // TODO a rule that triggers on something, just like the previous one, but it has "stop_processing" set to 1. // TODO a rule that triggers on that same thing, but it will not file, because the previous rule made FF skip it. From 7836feba63a07f1a4c91819a0f29226d763db80f Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:10:16 +0100 Subject: [PATCH 089/276] New trigger. --- config/firefly.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/firefly.php b/config/firefly.php index 2dbbae118b..53580cc889 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -169,6 +169,7 @@ return [ ], 'rule-triggers' => [ + 'user_action' => 'FireflyIII\Rules\Triggers', 'from_account_starts' => 'FireflyIII\Rules\Triggers', 'from_account_ends' => 'FireflyIII\Rules\Triggers', 'from_account_is' => 'FireflyIII\Rules\Triggers', From 4b350591406449227c1660d5fab6ad28c0e54b9a Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:37:48 +0100 Subject: [PATCH 090/276] Add connection to user. --- ...lCreated.php => TransactionJournalStored.php} | 15 ++++++++++++--- ...alSaved.php => TransactionJournalUpdated.php} | 4 ++-- app/User.php | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) rename app/Events/{JournalCreated.php => TransactionJournalStored.php} (64%) rename app/Events/{JournalSaved.php => TransactionJournalUpdated.php} (84%) diff --git a/app/Events/JournalCreated.php b/app/Events/TransactionJournalStored.php similarity index 64% rename from app/Events/JournalCreated.php rename to app/Events/TransactionJournalStored.php index 4b9ef60b9c..f03c7860bf 100644 --- a/app/Events/JournalCreated.php +++ b/app/Events/TransactionJournalStored.php @@ -1,15 +1,24 @@ -hasMany('FireflyIII\Models\Tag'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function rules() + { + return $this->hasMany('FireflyIII\Models\Rule'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function ruleGroups() + { + return $this->hasMany('FireflyIII\Models\RuleGroup'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ From 11f63fa6ce68dbf5cb0325d233f7eafd397e65f5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:38:05 +0100 Subject: [PATCH 091/276] Rename models for clarity --- app/Events/TransactionJournalStored.php | 4 ++-- app/Handlers/Events/ConnectJournalToPiggyBank.php | 8 ++++---- app/Handlers/Events/RescanJournal.php | 6 +++--- app/Handlers/Events/UpdateJournalConnection.php | 6 +++--- app/Http/Controllers/TransactionController.php | 10 +++++----- app/Providers/EventServiceProvider.php | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/Events/TransactionJournalStored.php b/app/Events/TransactionJournalStored.php index f03c7860bf..d2a0610485 100644 --- a/app/Events/TransactionJournalStored.php +++ b/app/Events/TransactionJournalStored.php @@ -1,6 +1,6 @@ journal; diff --git a/app/Handlers/Events/RescanJournal.php b/app/Handlers/Events/RescanJournal.php index 02926a1102..d8717803a6 100644 --- a/app/Handlers/Events/RescanJournal.php +++ b/app/Handlers/Events/RescanJournal.php @@ -1,6 +1,6 @@ journal; diff --git a/app/Handlers/Events/UpdateJournalConnection.php b/app/Handlers/Events/UpdateJournalConnection.php index 768a85f5bf..e04b44f088 100644 --- a/app/Handlers/Events/UpdateJournalConnection.php +++ b/app/Handlers/Events/UpdateJournalConnection.php @@ -1,6 +1,6 @@ journal; diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 54e9b5fc2b..e0f9bf0bf5 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -5,8 +5,8 @@ use Auth; use Carbon\Carbon; use Config; use ExpandedForm; -use FireflyIII\Events\JournalCreated; -use FireflyIII\Events\JournalSaved; +use FireflyIII\Events\TransactionJournalStored; +use FireflyIII\Events\TransactionJournalUpdated; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Http\Requests\JournalFormRequest; use FireflyIII\Models\PiggyBank; @@ -317,10 +317,10 @@ class TransactionController extends Controller } // rescan journal, UpdateJournalConnection - event(new JournalSaved($journal)); + event(new TransactionJournalUpdated($journal)); if ($journal->isTransfer() && intval($request->get('piggy_bank_id')) > 0) { - event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); + event(new TransactionJournalStored($journal, intval($request->get('piggy_bank_id')))); } Session::flash('success', 'New transaction "' . $journal->description . '" stored!'); @@ -369,7 +369,7 @@ class TransactionController extends Controller Session::flash('info', $att->getMessages()->get('attachments')); } - event(new JournalSaved($journal)); + event(new TransactionJournalUpdated($journal)); // update, get events by date and sort DESC Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.'); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index e76c561829..080b6e2877 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -29,12 +29,12 @@ class EventServiceProvider extends ServiceProvider */ protected $listen = [ - 'FireflyIII\Events\JournalSaved' => [ + 'FireflyIII\Events\TransactionJournalUpdated' => [ 'FireflyIII\Handlers\Events\RescanJournal', 'FireflyIII\Handlers\Events\UpdateJournalConnection', ], - 'FireflyIII\Events\JournalCreated' => [ + 'FireflyIII\Events\TransactionJournalStored' => [ 'FireflyIII\Handlers\Events\ConnectJournalToPiggyBank', ] ]; From b3a419b2f3db2f50516036e59f12d21fb6c93aa1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:38:12 +0100 Subject: [PATCH 092/276] New events. --- app/Handlers/Events/FireRulesForStore.php | 45 ++++++++++++++++++++++ app/Handlers/Events/FireRulesForUpdate.php | 40 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 app/Handlers/Events/FireRulesForStore.php create mode 100644 app/Handlers/Events/FireRulesForUpdate.php diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php new file mode 100644 index 0000000000..d3acea2e4d --- /dev/null +++ b/app/Handlers/Events/FireRulesForStore.php @@ -0,0 +1,45 @@ + Date: Tue, 12 Jan 2016 21:40:31 +0100 Subject: [PATCH 093/276] New test data. --- database/seeds/TestDataSeeder.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 4259fabf7d..c25c3391fd 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -65,12 +65,24 @@ class TestDataSeeder extends Seeder $rule->save(); - // trigger for this rule: + // initial trigger for this rule: $ruleTrigger = new RuleTrigger; $ruleTrigger->rule()->associate($rule); $ruleTrigger->order = 1; $ruleTrigger->active = 1; $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'create-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; $ruleTrigger->trigger_type = 'description_contains'; $ruleTrigger->trigger_value = 'groceries'; @@ -80,7 +92,7 @@ class TestDataSeeder extends Seeder // another $ruleTrigger = new RuleTrigger; $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 2; + $ruleTrigger->order = 3; $ruleTrigger->active = 1; $ruleTrigger->stop_processing = 0; $ruleTrigger->trigger_type = 'from_account_is'; From df5e3c9be9858065df9a3f161a882f4e08f39571 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:41:19 +0100 Subject: [PATCH 094/276] Include handlers. --- app/Providers/EventServiceProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 080b6e2877..9f623b00fd 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -32,10 +32,12 @@ class EventServiceProvider extends ServiceProvider 'FireflyIII\Events\TransactionJournalUpdated' => [ 'FireflyIII\Handlers\Events\RescanJournal', 'FireflyIII\Handlers\Events\UpdateJournalConnection', + 'FireflyIII\Handlers\Events\FireRulesForUpdate', ], 'FireflyIII\Events\TransactionJournalStored' => [ 'FireflyIII\Handlers\Events\ConnectJournalToPiggyBank', + 'FireflyIII\Handlers\Events\FireRulesForStore', ] ]; From 09f826cebad88b51d9fef75574ebdea79fd92507 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Jan 2016 21:41:45 +0100 Subject: [PATCH 095/276] First attempt at executing groups and rules. --- app/Handlers/Events/FireRulesForStore.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index d3acea2e4d..64b91a9ea3 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -11,6 +11,9 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\TransactionJournalStored; +use FireflyIII\Models\RuleGroup; +use FireflyIII\User; +use Illuminate\Support\Facades\Auth; /** * Class FireRulesForStore @@ -40,6 +43,14 @@ class FireRulesForStore public function handle(TransactionJournalStored $event) { // get all the user's rule groups, with the rules, order by 'order'. + /** @var User $user */ + $user = Auth::user(); + $groups = $user->ruleGroups()->with('rules')->orderBy('order','ASC')->get(); + + /** @var RuleGroup $group */ + foreach($groups as $group) { + + } } } \ No newline at end of file From 670ab059d18cd55a24a1f4485a71a9c8b6db557d Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 05:37:25 +0100 Subject: [PATCH 096/276] Simply log presence. --- app/Handlers/Events/FireRulesForStore.php | 20 +++++++++++--------- app/Handlers/Events/FireRulesForUpdate.php | 2 ++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 64b91a9ea3..b68c61157b 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -14,6 +14,7 @@ use FireflyIII\Events\TransactionJournalStored; use FireflyIII\Models\RuleGroup; use FireflyIII\User; use Illuminate\Support\Facades\Auth; +use Log; /** * Class FireRulesForStore @@ -43,14 +44,15 @@ class FireRulesForStore public function handle(TransactionJournalStored $event) { // get all the user's rule groups, with the rules, order by 'order'. - /** @var User $user */ - $user = Auth::user(); - - $groups = $user->ruleGroups()->with('rules')->orderBy('order','ASC')->get(); - - /** @var RuleGroup $group */ - foreach($groups as $group) { - - } +// /** @var User $user */ +// $user = Auth::user(); +// +// $groups = $user->ruleGroups()->with('rules')->orderBy('order','ASC')->get(); +// +// /** @var RuleGroup $group */ +// foreach($groups as $group) { +// +// } + Log::debug('FireRulesForStore!'); } } \ No newline at end of file diff --git a/app/Handlers/Events/FireRulesForUpdate.php b/app/Handlers/Events/FireRulesForUpdate.php index fc2da15d20..d0b36c45dd 100644 --- a/app/Handlers/Events/FireRulesForUpdate.php +++ b/app/Handlers/Events/FireRulesForUpdate.php @@ -9,6 +9,7 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\TransactionJournalUpdated; +use Log; /** * Class FireRulesForUpdate @@ -35,6 +36,7 @@ class FireRulesForUpdate */ public function handle(TransactionJournalUpdated $event) { + Log::debug('Fire rules for update!'); } } \ No newline at end of file From e722daafd0c801ff3877ef5f3b1d92207287293d Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 05:54:42 +0100 Subject: [PATCH 097/276] Removed old codeception file. --- codeception.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 codeception.yml diff --git a/codeception.yml b/codeception.yml deleted file mode 100644 index 3a8fca8145..0000000000 --- a/codeception.yml +++ /dev/null @@ -1,21 +0,0 @@ -actor: Tester -paths: - tests: tests - log: tests/_output - data: tests/_data - support: tests/_support - envs: tests/_envs -settings: - bootstrap: _bootstrap.php - colors: true - memory_limit: 1024M -extensions: - enabled: - - Codeception\Extension\RunFailed -modules: - config: - Db: - dsn: '' - user: '' - password: '' - dump: tests/_data/dump.sql From 98b272383f3ed98abffbf6668f4ddf686dddbcdb Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 07:16:29 +0100 Subject: [PATCH 098/276] Clean up triggers, add some new ones. --- .../Events/ConnectJournalToPiggyBank.php | 4 +- app/Handlers/Events/FireRulesForStore.php | 29 +++-- .../Events/ScanForBillsAfterStore.php | 61 ++++++++++ .../Events/ScanForBillsAfterUpdate.php | 61 ++++++++++ .../Controllers/TransactionController.php | 7 +- app/Providers/EventServiceProvider.php | 5 +- app/Rules/Processor.php | 105 ++++++++++++++++++ app/Support/Domain.php | 8 ++ 8 files changed, 262 insertions(+), 18 deletions(-) create mode 100644 app/Handlers/Events/ScanForBillsAfterStore.php create mode 100644 app/Handlers/Events/ScanForBillsAfterUpdate.php create mode 100644 app/Rules/Processor.php diff --git a/app/Handlers/Events/ConnectJournalToPiggyBank.php b/app/Handlers/Events/ConnectJournalToPiggyBank.php index 495d112cae..0b6c96da8a 100644 --- a/app/Handlers/Events/ConnectJournalToPiggyBank.php +++ b/app/Handlers/Events/ConnectJournalToPiggyBank.php @@ -42,12 +42,12 @@ class ConnectJournalToPiggyBank $piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); if (is_null($piggyBank)) { - return false; + return true; } // update piggy bank rep for date of transaction journal. $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); if (is_null($repetition)) { - return false; + return true; } bcscale(2); diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index b68c61157b..1220f4ed10 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -11,7 +11,9 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\TransactionJournalStored; +use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; +use FireflyIII\Rules\Processor; use FireflyIII\User; use Illuminate\Support\Facades\Auth; use Log; @@ -44,15 +46,24 @@ class FireRulesForStore public function handle(TransactionJournalStored $event) { // get all the user's rule groups, with the rules, order by 'order'. -// /** @var User $user */ -// $user = Auth::user(); -// -// $groups = $user->ruleGroups()->with('rules')->orderBy('order','ASC')->get(); -// -// /** @var RuleGroup $group */ -// foreach($groups as $group) { -// -// } + /** @var User $user */ + $user = Auth::user(); + $groups = $user->ruleGroups()->with('rules')->hasTrigger('user_action','store-journal')->orderBy('order', 'ASC')->get(); + // + /** @var RuleGroup $group */ + foreach ($groups as $group) { + $rules = $group->rules; + /** @var Rule $rule */ + foreach ($rules as $rule) { + $processor = new Processor($rule, $event->journal); + + // get some return out of this? + $processor->handle(); + + } + } Log::debug('FireRulesForStore!'); + echo 'handle'; + exit; } } \ No newline at end of file diff --git a/app/Handlers/Events/ScanForBillsAfterStore.php b/app/Handlers/Events/ScanForBillsAfterStore.php new file mode 100644 index 0000000000..26f807fac1 --- /dev/null +++ b/app/Handlers/Events/ScanForBillsAfterStore.php @@ -0,0 +1,61 @@ +journal; + + Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')'); + + /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); + $list = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get(); + + Log::debug('Found ' . $list->count() . ' bills to check.'); + + /** @var \FireflyIII\Models\Bill $bill */ + foreach ($list as $bill) { + Log::debug('Now calling bill #' . $bill->id . ' (' . $bill->name . ')'); + $repository->scan($bill, $journal); + } + + Log::debug('Done!'); + } + +} diff --git a/app/Handlers/Events/ScanForBillsAfterUpdate.php b/app/Handlers/Events/ScanForBillsAfterUpdate.php new file mode 100644 index 0000000000..424e1c0e84 --- /dev/null +++ b/app/Handlers/Events/ScanForBillsAfterUpdate.php @@ -0,0 +1,61 @@ +journal; + + Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')'); + + /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); + $list = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get(); + + Log::debug('Found ' . $list->count() . ' bills to check.'); + + /** @var \FireflyIII\Models\Bill $bill */ + foreach ($list as $bill) { + Log::debug('Now calling bill #' . $bill->id . ' (' . $bill->name . ')'); + $repository->scan($bill, $journal); + } + + Log::debug('Done!'); + } + +} diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index e0f9bf0bf5..d760b9dbe6 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -316,12 +316,7 @@ class TransactionController extends Controller Session::flash('info', $att->getMessages()->get('attachments')); } - // rescan journal, UpdateJournalConnection - event(new TransactionJournalUpdated($journal)); - - if ($journal->isTransfer() && intval($request->get('piggy_bank_id')) > 0) { - event(new TransactionJournalStored($journal, intval($request->get('piggy_bank_id')))); - } + event(new TransactionJournalStored($journal, intval($request->get('piggy_bank_id')))); Session::flash('success', 'New transaction "' . $journal->description . '" stored!'); Preferences::mark(); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 9f623b00fd..7b86a54d04 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -30,12 +30,15 @@ class EventServiceProvider extends ServiceProvider protected $listen = [ 'FireflyIII\Events\TransactionJournalUpdated' => [ - 'FireflyIII\Handlers\Events\RescanJournal', + 'FireflyIII\Handlers\Events\ScanForBillsAfterUpdate', + 'FireflyIII\Handlers\Events\UpdateJournalConnection', 'FireflyIII\Handlers\Events\FireRulesForUpdate', ], 'FireflyIII\Events\TransactionJournalStored' => [ + 'FireflyIII\Handlers\Events\ScanForBillsAfterStore', + 'FireflyIII\Handlers\Events\ConnectJournalToPiggyBank', 'FireflyIII\Handlers\Events\FireRulesForStore', ] diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php new file mode 100644 index 0000000000..870fc1ecd6 --- /dev/null +++ b/app/Rules/Processor.php @@ -0,0 +1,105 @@ +rule = $rule; + $this->journal = $journal; + $this->triggerTypes = Domain::getRuleTriggers(); + } + + public function handle() + { + // get all triggers: + $triggered = $this->triggered(); + if ($triggered) { + + } + + } + + /** + * @return bool + */ + protected function triggered() + { + /** @var RuleTrigger $trigger */ + foreach ($this->rule->ruleTriggers as $trigger) { + $type = $trigger->trigger_type; + $class = $this->triggerTypes[$type]; + if (!class_exists($class)) { + throw new Exception('Could not instantiate class for rule trigger type "' . $type . '".'); + } + } + + + return true; + + } + + /** + * @return Rule + */ + public function getRule() + { + return $this->rule; + } + + /** + * @param Rule $rule + */ + public function setRule($rule) + { + $this->rule = $rule; + } + + /** + * @return TransactionJournal + */ + public function getJournal() + { + return $this->journal; + } + + /** + * @param TransactionJournal $journal + */ + public function setJournal($journal) + { + $this->journal = $journal; + } + + +} \ No newline at end of file diff --git a/app/Support/Domain.php b/app/Support/Domain.php index 53b9012298..bf4616f6f3 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -26,4 +26,12 @@ class Domain return Config::get('firefly.bindables'); } + + /** + * @return array + */ + public static function getRuleTriggers() + { + return Config::get('firefly.rule-triggers'); + } } \ No newline at end of file From 0dc74d9d14b1dcfbb977270e1b5dcb9ce2865585 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 07:30:11 +0100 Subject: [PATCH 099/276] Rules will now fire for a store-action. And fail. --- app/Handlers/Events/FireRulesForStore.php | 11 +++++++++-- app/Models/Rule.php | 21 +++++++++++++++++++-- app/Rules/Processor.php | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 1220f4ed10..782e244bec 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -15,6 +15,7 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; use FireflyIII\Rules\Processor; use FireflyIII\User; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\Auth; use Log; @@ -48,7 +49,13 @@ class FireRulesForStore // get all the user's rule groups, with the rules, order by 'order'. /** @var User $user */ $user = Auth::user(); - $groups = $user->ruleGroups()->with('rules')->hasTrigger('user_action','store-journal')->orderBy('order', 'ASC')->get(); + $groups = $user->ruleGroups()->with( + [ + 'rules' => function (HasMany $query) { + $query->hasTrigger('user_action', 'store-journal'); + } + ] + )->orderBy('order', 'ASC')->get(); // /** @var RuleGroup $group */ foreach ($groups as $group) { @@ -63,7 +70,7 @@ class FireRulesForStore } } Log::debug('FireRulesForStore!'); - echo 'handle'; + echo 'done handling rules.'; exit; } } \ No newline at end of file diff --git a/app/Models/Rule.php b/app/Models/Rule.php index c4c73aac1c..55a6f97ab5 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -9,7 +9,7 @@ namespace FireflyIII\Models; -use Crypt; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; /** @@ -34,7 +34,6 @@ use Illuminate\Database\Eloquent\Model; */ class Rule extends Model { - /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -67,5 +66,23 @@ class Rule extends Model return $this->hasMany('FireflyIII\Models\RuleTrigger'); } + /** + * @param $query + * @param $triggerType + * @param null $triggerValue + * + * @return Builder + */ + public function scopeHasTrigger(Builder $query, $triggerType, $triggerValue = null) + { + $query->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id'); + $query->where('rule_triggers.trigger_type', $triggerType); + if (!is_null($triggerValue)) { + $query->where('rule_triggers.trigger_value', $triggerValue); + } + return $query; + + } + } diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index 870fc1ecd6..ef44b53af0 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -60,7 +60,7 @@ class Processor $type = $trigger->trigger_type; $class = $this->triggerTypes[$type]; if (!class_exists($class)) { - throw new Exception('Could not instantiate class for rule trigger type "' . $type . '".'); + abort(500, 'Could not instantiate class for rule trigger type "' . $type . '" ('.$class.').'); } } From 1087b9f5df3d7c4735355b681accf5beb2021a1a Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 07:47:26 +0100 Subject: [PATCH 100/276] Can now trigger. --- app/Handlers/Events/FireRulesForStore.php | 1 + app/Rules/Processor.php | 25 +++++++-- app/Rules/Triggers/DescriptionContains.php | 65 ++++++++++++++++++++++ app/Rules/Triggers/FromAccountIs.php | 60 ++++++++++++++++++++ app/Rules/Triggers/TriggerInterface.php | 34 +++++++++++ app/Rules/Triggers/UserAction.php | 54 ++++++++++++++++++ config/firefly.php | 6 +- database/seeds/TestDataSeeder.php | 2 +- 8 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 app/Rules/Triggers/DescriptionContains.php create mode 100644 app/Rules/Triggers/FromAccountIs.php create mode 100644 app/Rules/Triggers/TriggerInterface.php create mode 100644 app/Rules/Triggers/UserAction.php diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 782e244bec..39435cd1a8 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -62,6 +62,7 @@ class FireRulesForStore $rules = $group->rules; /** @var Rule $rule */ foreach ($rules as $rule) { + Log::debug('Now handling rule #' . $rule->id); $processor = new Processor($rule, $event->journal); // get some return out of this? diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index ef44b53af0..0f58d9015f 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -9,11 +9,12 @@ namespace FireflyIII\Rules; -use Exception; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Rules\Triggers\TriggerInterface; use FireflyIII\Support\Domain; +use Log; /** * Class Processor @@ -45,27 +46,39 @@ class Processor // get all triggers: $triggered = $this->triggered(); if ($triggered) { - + Log::debug('Rule #' . $this->rule->id . ' was triggered. Now process each action.'); } } /** + * TODO stop when stop_processing is present. + * * @return bool */ protected function triggered() { + $foundTriggers = 0; + $hitTriggers = 0; /** @var RuleTrigger $trigger */ - foreach ($this->rule->ruleTriggers as $trigger) { + foreach ($this->rule->ruleTriggers()->orderBy('order', 'ASC')->get() as $index => $trigger) { + $foundTriggers++; $type = $trigger->trigger_type; $class = $this->triggerTypes[$type]; + Log::debug('Trigger #' . $trigger->id . ' for rule #' . $trigger->rule_id . ' (' . $type . ')'); if (!class_exists($class)) { - abort(500, 'Could not instantiate class for rule trigger type "' . $type . '" ('.$class.').'); + abort(500, 'Could not instantiate class for rule trigger type "' . $type . '" (' . $class . ').'); } + /** @var TriggerInterface $triggerClass */ + $triggerClass = new $class($trigger, $this->journal); + if ($triggerClass->triggered()) { + $hitTriggers++; + } + } + Log::debug('Total: ' . $foundTriggers . ' found triggers. ' . $hitTriggers . ' triggers were hit.'); - - return true; + return ($hitTriggers == $foundTriggers); } diff --git a/app/Rules/Triggers/DescriptionContains.php b/app/Rules/Triggers/DescriptionContains.php new file mode 100644 index 0000000000..e55b850d15 --- /dev/null +++ b/app/Rules/Triggers/DescriptionContains.php @@ -0,0 +1,65 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $search = strtolower($this->trigger->trigger_value); + $source = strtolower($this->journal->description); + + $strpos = strpos($source, $search); + if (!($strpos === false)) { + // found something + Log::debug('"' . $source . '" contains the text "' . $search . '". Return true.'); + + return true; + } + + // found nothing. + Log::debug('"' . $source . '" does not contain the text "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/FromAccountIs.php b/app/Rules/Triggers/FromAccountIs.php new file mode 100644 index 0000000000..27de26653d --- /dev/null +++ b/app/Rules/Triggers/FromAccountIs.php @@ -0,0 +1,60 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $fromAccountName = strtolower($this->journal->source_account->name); + $search = strtolower($this->trigger->trigger_value); + + if ($fromAccountName == $search) { + Log::debug('"' . $fromAccountName . '" equals "' . $search . '" exactly. Return true.'); + + return true; + } + Log::debug('"' . $fromAccountName . '" does not equal "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/TriggerInterface.php b/app/Rules/Triggers/TriggerInterface.php new file mode 100644 index 0000000000..094ee0f234 --- /dev/null +++ b/app/Rules/Triggers/TriggerInterface.php @@ -0,0 +1,34 @@ +trigger = $trigger; + $this->journal = $journal; + + } + + /** + * This trigger is always triggered, because the rule that it is a part of has been pre-selected on this condition. + * + * @return bool + */ + public function triggered() + { + Log::debug('user_action always returns true.'); + return true; + } + +} \ No newline at end of file diff --git a/config/firefly.php b/config/firefly.php index 53580cc889..e1b535fd30 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -169,10 +169,10 @@ return [ ], 'rule-triggers' => [ - 'user_action' => 'FireflyIII\Rules\Triggers', + 'user_action' => 'FireflyIII\Rules\Triggers\UserAction', 'from_account_starts' => 'FireflyIII\Rules\Triggers', 'from_account_ends' => 'FireflyIII\Rules\Triggers', - 'from_account_is' => 'FireflyIII\Rules\Triggers', + 'from_account_is' => 'FireflyIII\Rules\Triggers\FromAccountIs', 'from_account_contains' => 'FireflyIII\Rules\Triggers', 'to_account_starts' => 'FireflyIII\Rules\Triggers', 'to_account_ends' => 'FireflyIII\Rules\Triggers', @@ -185,7 +185,7 @@ return [ 'amount_more' => 'FireflyIII\Rules\Triggers', 'description_starts' => 'FireflyIII\Rules\Triggers', 'description_ends' => 'FireflyIII\Rules\Triggers', - 'description_contains' => 'FireflyIII\Rules\Triggers', + 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', 'description_is' => 'FireflyIII\Rules\Triggers', ], 'rule-actions' => [ diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index c25c3391fd..bb6ce4c74e 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -72,7 +72,7 @@ class TestDataSeeder extends Seeder $ruleTrigger->active = 1; $ruleTrigger->stop_processing = 0; $ruleTrigger->trigger_type = 'user_action'; - $ruleTrigger->trigger_value = 'create-journal'; + $ruleTrigger->trigger_value = 'store-journal'; $ruleTrigger->save(); unset($ruleTrigger); From ce250c85fc26d0cd46d7fc2e9f03b81d81bfe603 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 07:48:43 +0100 Subject: [PATCH 101/276] Rename class. --- app/Handlers/Events/FireRulesForStore.php | 4 ++-- app/Rules/{Processor.php => TriggerProcessor.php} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename app/Rules/{Processor.php => TriggerProcessor.php} (96%) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 39435cd1a8..55b343aae4 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -13,7 +13,7 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\TransactionJournalStored; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; -use FireflyIII\Rules\Processor; +use FireflyIII\Rules\TriggerProcessor; use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\Auth; @@ -63,7 +63,7 @@ class FireRulesForStore /** @var Rule $rule */ foreach ($rules as $rule) { Log::debug('Now handling rule #' . $rule->id); - $processor = new Processor($rule, $event->journal); + $processor = new TriggerProcessor($rule, $event->journal); // get some return out of this? $processor->handle(); diff --git a/app/Rules/Processor.php b/app/Rules/TriggerProcessor.php similarity index 96% rename from app/Rules/Processor.php rename to app/Rules/TriggerProcessor.php index 0f58d9015f..216f05b178 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/TriggerProcessor.php @@ -1,6 +1,6 @@ Date: Wed, 13 Jan 2016 08:14:14 +0100 Subject: [PATCH 102/276] Can now automatically handle some rules. No user interface, yet. --- app/Handlers/Events/FireRulesForStore.php | 7 +- app/Models/Category.php | 1 - app/Rules/Actions/ActionInterface.php | 33 ++++++++++ app/Rules/Actions/SetBudget.php | 64 +++++++++++++++++++ app/Rules/Actions/SetCategory.php | 54 ++++++++++++++++ .../{TriggerProcessor.php => Processor.php} | 41 ++++++++++-- app/Support/Domain.php | 8 +++ config/firefly.php | 4 +- 8 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 app/Rules/Actions/ActionInterface.php create mode 100644 app/Rules/Actions/SetBudget.php create mode 100644 app/Rules/Actions/SetCategory.php rename app/Rules/{TriggerProcessor.php => Processor.php} (70%) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 55b343aae4..dcad94713c 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -13,7 +13,7 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\TransactionJournalStored; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; -use FireflyIII\Rules\TriggerProcessor; +use FireflyIII\Rules\Processor; use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\Auth; @@ -63,15 +63,12 @@ class FireRulesForStore /** @var Rule $rule */ foreach ($rules as $rule) { Log::debug('Now handling rule #' . $rule->id); - $processor = new TriggerProcessor($rule, $event->journal); + $processor = new Processor($rule, $event->journal); // get some return out of this? $processor->handle(); } } - Log::debug('FireRulesForStore!'); - echo 'done handling rules.'; - exit; } } \ No newline at end of file diff --git a/app/Models/Category.php b/app/Models/Category.php index cca73fe416..b331b5960a 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -42,7 +42,6 @@ class Category extends Model unset($search['name']); foreach ($search as $name => $value) { $query->where($name, $value); - } $set = $query->get(['categories.*']); /** @var Category $category */ diff --git a/app/Rules/Actions/ActionInterface.php b/app/Rules/Actions/ActionInterface.php new file mode 100644 index 0000000000..16a597b7e1 --- /dev/null +++ b/app/Rules/Actions/ActionInterface.php @@ -0,0 +1,33 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + /** @var BudgetRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $search = $this->action->action_value; + $budgets = $repository->getActiveBudgets(); + $budget = $budgets->filter( + function (Budget $current) use ($search) { + return $current->name == $search; + } + )->first(); + if (!is_null($budget)) { + Log::debug('Will set budget "' . $search . '" (#' . $budget->id . ') on journal #' . $this->journal->id . '.'); + $this->journal->budgets()->save($budget); + } else { + Log::debug('Could not find budget "'.$search.'". Failed.'); + } + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/SetCategory.php b/app/Rules/Actions/SetCategory.php new file mode 100644 index 0000000000..764bfdc4d6 --- /dev/null +++ b/app/Rules/Actions/SetCategory.php @@ -0,0 +1,54 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $name = $this->action->action_value; + $category = Category::firstOrCreateEncrypted(['name' => $name, 'user_id' => Auth::user()->id]); + Log::debug('Will set category "' . $name . '" (#' . $category->id . ') on journal #' . $this->journal->id . '.'); + $this->journal->categories()->save($category); + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/TriggerProcessor.php b/app/Rules/Processor.php similarity index 70% rename from app/Rules/TriggerProcessor.php rename to app/Rules/Processor.php index 216f05b178..310c73d898 100644 --- a/app/Rules/TriggerProcessor.php +++ b/app/Rules/Processor.php @@ -1,6 +1,6 @@ rule = $rule; $this->journal = $journal; $this->triggerTypes = Domain::getRuleTriggers(); + $this->actionTypes = Domain::getRuleActions(); } public function handle() @@ -47,6 +54,7 @@ class TriggerProcessor $triggered = $this->triggered(); if ($triggered) { Log::debug('Rule #' . $this->rule->id . ' was triggered. Now process each action.'); + $this->actions(); } } @@ -82,6 +90,31 @@ class TriggerProcessor } + /** + * @return bool + */ + protected function actions() + { + /** + * @var int $index + * @var RuleAction $action + */ + foreach ($this->rule->ruleActions()->orderBy('order', 'ASC')->get() as $index => $action) { + $type = $action->action_type; + $class = $this->actionTypes[$type]; + Log::debug('Action #' . $action->id . ' for rule #' . $action->rule_id . ' (' . $type . ')'); + if (!class_exists($class)) { + abort(500, 'Could not instantiate class for rule action type "' . $type . '" (' . $class . ').'); + } + /** @var ActionInterface $actionClass */ + $actionClass = new $class($action, $this->journal); + $actionClass->act(); + + } + + return true; + } + /** * @return Rule */ diff --git a/app/Support/Domain.php b/app/Support/Domain.php index bf4616f6f3..d1f8be1fed 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -34,4 +34,12 @@ class Domain { return Config::get('firefly.rule-triggers'); } + + /** + * @return array + */ + public static function getRuleActions() + { + return Config::get('firefly.rule-actions'); + } } \ No newline at end of file diff --git a/config/firefly.php b/config/firefly.php index e1b535fd30..469a6203b7 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -189,9 +189,9 @@ return [ 'description_is' => 'FireflyIII\Rules\Triggers', ], 'rule-actions' => [ - 'set_category' => 'FireflyIII\Rules\Actions', + 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', 'clear_category' => 'FireflyIII\Rules\Actions', - 'set_budget' => 'FireflyIII\Rules\Actions', + 'set_budget' => 'FireflyIII\Rules\Actions\SetBudget', 'clear_budget' => 'FireflyIII\Rules\Actions', 'add_tag' => 'FireflyIII\Rules\Actions', 'remove_tag' => 'FireflyIII\Rules\Actions', From cf4d7cfeefb7bd0af3d59955aeca67774506a4c7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 09:08:13 +0100 Subject: [PATCH 103/276] Grab rules properly. --- app/Handlers/Events/FireRulesForStore.php | 15 ++++++--------- app/Models/Rule.php | 19 ------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index dcad94713c..7c1723004c 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -15,7 +15,6 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; use FireflyIII\Rules\Processor; use FireflyIII\User; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\Auth; use Log; @@ -49,17 +48,15 @@ class FireRulesForStore // get all the user's rule groups, with the rules, order by 'order'. /** @var User $user */ $user = Auth::user(); - $groups = $user->ruleGroups()->with( - [ - 'rules' => function (HasMany $query) { - $query->hasTrigger('user_action', 'store-journal'); - } - ] - )->orderBy('order', 'ASC')->get(); + $groups = $user->ruleGroups()->orderBy('order', 'ASC')->get(); // /** @var RuleGroup $group */ foreach ($groups as $group) { - $rules = $group->rules; + $rules = $group->rules() + ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') + ->where('rule_triggers.trigger_type', 'user_action') + ->where('rule_triggers.trigger_value', 'store-journal') + ->get(['rules.*']); /** @var Rule $rule */ foreach ($rules as $rule) { Log::debug('Now handling rule #' . $rule->id); diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 55a6f97ab5..7919401c5e 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -66,23 +66,4 @@ class Rule extends Model return $this->hasMany('FireflyIII\Models\RuleTrigger'); } - /** - * @param $query - * @param $triggerType - * @param null $triggerValue - * - * @return Builder - */ - public function scopeHasTrigger(Builder $query, $triggerType, $triggerValue = null) - { - $query->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id'); - $query->where('rule_triggers.trigger_type', $triggerType); - if (!is_null($triggerValue)) { - $query->where('rule_triggers.trigger_value', $triggerValue); - } - return $query; - - } - - } From ae3258b449f553e99ea75dd4c5a43d7f102181db Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:01:09 +0100 Subject: [PATCH 104/276] If you get the source account attribute before the model is saved, the cache breaks. --- app/Models/TransactionJournal.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 07f4048b47..4e6e3a6dff 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -308,16 +308,8 @@ class TransactionJournal extends Model */ public function getSourceAccountAttribute() { - $cache = new CacheProperties; - $cache->addProperty($this->id); - $cache->addProperty('sourceAccount'); - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } $account = $this->transactions()->where('amount', '<', 0)->first()->account; - $cache->store($account); - return $account; } From 88bbafd3e8db836c703d4834eafbe9968bc968c9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:01:40 +0100 Subject: [PATCH 105/276] If you get the destination account attribute before the model is saved, the cache breaks. --- app/Models/TransactionJournal.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 4e6e3a6dff..8ec64eceb1 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -290,16 +290,8 @@ class TransactionJournal extends Model */ public function getDestinationAccountAttribute() { - $cache = new CacheProperties; - $cache->addProperty($this->id); - $cache->addProperty('destinationAccount'); - - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } $account = $this->transactions()->where('amount', '>', 0)->first()->account; - $cache->store($account); - + return $account; } From a7a00ecf409f77cf0eff1ca4e7806b2dc39ceec1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:02:22 +0100 Subject: [PATCH 106/276] More rule possibilities --- app/Handlers/Events/FireRulesForStore.php | 6 +- .../Controllers/TransactionController.php | 2 + app/Models/TransactionJournal.php | 2 +- app/Rules/Processor.php | 7 +- app/Rules/Triggers/FromAccountEnds.php | 74 +++++++++++++++++++ app/Rules/Triggers/FromAccountStarts.php | 62 ++++++++++++++++ config/firefly.php | 4 +- 7 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 app/Rules/Triggers/FromAccountEnds.php create mode 100644 app/Rules/Triggers/FromAccountStarts.php diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 7c1723004c..37506ca9dd 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -45,6 +45,7 @@ class FireRulesForStore */ public function handle(TransactionJournalStored $event) { + Log::debug('Before event (in handle). From account name is: ' . $event->journal->source_account->name); // get all the user's rule groups, with the rules, order by 'order'. /** @var User $user */ $user = Auth::user(); @@ -52,6 +53,7 @@ class FireRulesForStore // /** @var RuleGroup $group */ foreach ($groups as $group) { + Log::debug('Now processing group "' . $group->title . '".'); $rules = $group->rules() ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') ->where('rule_triggers.trigger_type', 'user_action') @@ -59,7 +61,7 @@ class FireRulesForStore ->get(['rules.*']); /** @var Rule $rule */ foreach ($rules as $rule) { - Log::debug('Now handling rule #' . $rule->id); + Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title. ')'); $processor = new Processor($rule, $event->journal); // get some return out of this? @@ -67,5 +69,7 @@ class FireRulesForStore } } +// echo 'Done processing rules. See log.'; +// exit; } } \ No newline at end of file diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index d760b9dbe6..a5009e0cca 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -18,6 +18,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; use Input; +use Log; use Preferences; use Response; use Session; @@ -315,6 +316,7 @@ class TransactionController extends Controller if (count($att->getMessages()->get('attachments')) > 0) { Session::flash('info', $att->getMessages()->get('attachments')); } + Log::debug('Before event. From account name is: ' . $journal->source_account->name); event(new TransactionJournalStored($journal, intval($request->get('piggy_bank_id')))); diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 8ec64eceb1..d060021302 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -291,7 +291,7 @@ class TransactionJournal extends Model public function getDestinationAccountAttribute() { $account = $this->transactions()->where('amount', '>', 0)->first()->account; - + return $account; } diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index 310c73d898..9ef84fa551 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -71,7 +71,12 @@ class Processor /** @var RuleTrigger $trigger */ foreach ($this->rule->ruleTriggers()->orderBy('order', 'ASC')->get() as $index => $trigger) { $foundTriggers++; - $type = $trigger->trigger_type; + $type = $trigger->trigger_type; + + if (!isset($this->triggerTypes[$type])) { + abort(500, 'No such trigger exists ("' . $type . '").'); + } + $class = $this->triggerTypes[$type]; Log::debug('Trigger #' . $trigger->id . ' for rule #' . $trigger->rule_id . ' (' . $type . ')'); if (!class_exists($class)) { diff --git a/app/Rules/Triggers/FromAccountEnds.php b/app/Rules/Triggers/FromAccountEnds.php new file mode 100644 index 0000000000..b207ef6ddd --- /dev/null +++ b/app/Rules/Triggers/FromAccountEnds.php @@ -0,0 +1,74 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $fromAccountName = strtolower($this->journal->source_account->name); + $fromAccountNameLength = strlen($fromAccountName); + $search = strtolower($this->trigger->trigger_value); + $searchLength = strlen($search); + + // if the string to search for is longer than the account name, + // shorten the search string. + if ($searchLength > $fromAccountNameLength) { + Log::debug('Search string "' . $search . '" (' . $searchLength . ') is longer than "' . $fromAccountName . '" (' . $fromAccountNameLength . '). '); + $search = substr($search, ($fromAccountNameLength * -1)); + $searchLength = strlen($search); + Log::debug('Search string is now "' . $search . '" (' . $searchLength . ') instead.'); + } + + + $part = substr($fromAccountName, $searchLength * -1); + + if ($part == $search) { + Log::debug('"' . $fromAccountName . '" ends with "' . $search . '". Return true.'); + + return true; + } + Log::debug('"' . $fromAccountName . '" does not end with "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/FromAccountStarts.php b/app/Rules/Triggers/FromAccountStarts.php new file mode 100644 index 0000000000..a5206026ea --- /dev/null +++ b/app/Rules/Triggers/FromAccountStarts.php @@ -0,0 +1,62 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $fromAccountName = strtolower($this->journal->source_account->name); + $search = strtolower($this->trigger->trigger_value); + + $part = substr($fromAccountName, 0, strlen($search)); + + if ($part == $search) { + Log::debug('"' . $fromAccountName . '" starts with "' . $search . '". Return true.'); + + return true; + } + Log::debug('"' . $fromAccountName . '" does not start with "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/config/firefly.php b/config/firefly.php index 469a6203b7..bd323ebfc0 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -170,8 +170,8 @@ return [ 'rule-triggers' => [ 'user_action' => 'FireflyIII\Rules\Triggers\UserAction', - 'from_account_starts' => 'FireflyIII\Rules\Triggers', - 'from_account_ends' => 'FireflyIII\Rules\Triggers', + 'from_account_starts' => 'FireflyIII\Rules\Triggers\FromAccountStarts', + 'from_account_ends' => 'FireflyIII\Rules\Triggers\FromAccountEnds', 'from_account_is' => 'FireflyIII\Rules\Triggers\FromAccountIs', 'from_account_contains' => 'FireflyIII\Rules\Triggers', 'to_account_starts' => 'FireflyIII\Rules\Triggers', From 8720511046ba541adddc8c4e51162f114e2a09eb Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:05:26 +0100 Subject: [PATCH 107/276] From account contains trigger. --- app/Rules/Triggers/FromAccountContains.php | 64 ++++++++++++++++++++++ config/firefly.php | 2 +- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 app/Rules/Triggers/FromAccountContains.php diff --git a/app/Rules/Triggers/FromAccountContains.php b/app/Rules/Triggers/FromAccountContains.php new file mode 100644 index 0000000000..5e1e990cb6 --- /dev/null +++ b/app/Rules/Triggers/FromAccountContains.php @@ -0,0 +1,64 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $fromAccountName = strtolower($this->journal->source_account->name); + $search = strtolower($this->trigger->trigger_value); + $strpos = strpos($fromAccountName, $search); + + if (!($strpos === false)) { + // found something + Log::debug('"' . $fromAccountName . '" contains the text "' . $search . '". Return true.'); + + return true; + } + + // found nothing. + Log::debug('"' . $fromAccountName . '" does not contain the text "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/config/firefly.php b/config/firefly.php index bd323ebfc0..ef85e627f6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -173,7 +173,7 @@ return [ 'from_account_starts' => 'FireflyIII\Rules\Triggers\FromAccountStarts', 'from_account_ends' => 'FireflyIII\Rules\Triggers\FromAccountEnds', 'from_account_is' => 'FireflyIII\Rules\Triggers\FromAccountIs', - 'from_account_contains' => 'FireflyIII\Rules\Triggers', + 'from_account_contains' => 'FireflyIII\Rules\Triggers\FromAccountContains', 'to_account_starts' => 'FireflyIII\Rules\Triggers', 'to_account_ends' => 'FireflyIII\Rules\Triggers', 'to_account_is' => 'FireflyIII\Rules\Triggers', From afec8480fbdec69aaf135b3d10d129d7f1a5a881 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:12:46 +0100 Subject: [PATCH 108/276] More working triggers. --- app/Rules/Triggers/ToAccountContains.php | 64 ++++++++++++++++++++ app/Rules/Triggers/ToAccountEnds.php | 74 ++++++++++++++++++++++++ app/Rules/Triggers/ToAccountIs.php | 60 +++++++++++++++++++ app/Rules/Triggers/ToAccountStarts.php | 62 ++++++++++++++++++++ app/Rules/Triggers/TransactionType.php | 59 +++++++++++++++++++ app/Rules/Triggers/UserAction.php | 1 + config/firefly.php | 10 ++-- 7 files changed, 325 insertions(+), 5 deletions(-) create mode 100644 app/Rules/Triggers/ToAccountContains.php create mode 100644 app/Rules/Triggers/ToAccountEnds.php create mode 100644 app/Rules/Triggers/ToAccountIs.php create mode 100644 app/Rules/Triggers/ToAccountStarts.php create mode 100644 app/Rules/Triggers/TransactionType.php diff --git a/app/Rules/Triggers/ToAccountContains.php b/app/Rules/Triggers/ToAccountContains.php new file mode 100644 index 0000000000..5e06383f04 --- /dev/null +++ b/app/Rules/Triggers/ToAccountContains.php @@ -0,0 +1,64 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $toAccountName = strtolower($this->journal->destination_account->name); + $search = strtolower($this->trigger->trigger_value); + $strpos = strpos($toAccountName, $search); + + if (!($strpos === false)) { + // found something + Log::debug('"' . $toAccountName . '" contains the text "' . $search . '". Return true.'); + + return true; + } + + // found nothing. + Log::debug('"' . $toAccountName . '" does not contain the text "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/ToAccountEnds.php b/app/Rules/Triggers/ToAccountEnds.php new file mode 100644 index 0000000000..67bb9750f1 --- /dev/null +++ b/app/Rules/Triggers/ToAccountEnds.php @@ -0,0 +1,74 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $toAccountName = strtolower($this->journal->destination_account->name); + $toAccountNameLength = strlen($toAccountName); + $search = strtolower($this->trigger->trigger_value); + $searchLength = strlen($search); + + // if the string to search for is longer than the account name, + // shorten the search string. + if ($searchLength > $toAccountNameLength) { + Log::debug('Search string "' . $search . '" (' . $searchLength . ') is longer than "' . $toAccountName . '" (' . $toAccountNameLength . '). '); + $search = substr($search, ($toAccountNameLength * -1)); + $searchLength = strlen($search); + Log::debug('Search string is now "' . $search . '" (' . $searchLength . ') instead.'); + } + + + $part = substr($toAccountName, $searchLength * -1); + + if ($part == $search) { + Log::debug('"' . $toAccountName . '" ends with "' . $search . '". Return true.'); + + return true; + } + Log::debug('"' . $toAccountName . '" does not end with "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/ToAccountIs.php b/app/Rules/Triggers/ToAccountIs.php new file mode 100644 index 0000000000..09f4c66f6a --- /dev/null +++ b/app/Rules/Triggers/ToAccountIs.php @@ -0,0 +1,60 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $toAccountName = strtolower($this->journal->destination_account->name); + $search = strtolower($this->trigger->trigger_value); + + if ($toAccountName == $search) { + Log::debug('"' . $toAccountName . '" equals "' . $search . '" exactly. Return true.'); + + return true; + } + Log::debug('"' . $toAccountName . '" does not equal "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/ToAccountStarts.php b/app/Rules/Triggers/ToAccountStarts.php new file mode 100644 index 0000000000..0af5fc4491 --- /dev/null +++ b/app/Rules/Triggers/ToAccountStarts.php @@ -0,0 +1,62 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $toAccountName = strtolower($this->journal->destination_account->name); + $search = strtolower($this->trigger->trigger_value); + + $part = substr($toAccountName, 0, strlen($search)); + + if ($part == $search) { + Log::debug('"' . $toAccountName . '" starts with "' . $search . '". Return true.'); + + return true; + } + Log::debug('"' . $toAccountName . '" does not start with "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/TransactionType.php b/app/Rules/Triggers/TransactionType.php new file mode 100644 index 0000000000..9bba776f42 --- /dev/null +++ b/app/Rules/Triggers/TransactionType.php @@ -0,0 +1,59 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $type = strtolower($this->journal->transactionType->type); + $search = strtolower($this->trigger->trigger_value); + + if ($type == $search) { + Log::debug('Journal is of type "' . $type . '" which matches with "' . $search . '". Return true'); + + return true; + } + Log::debug('Journal is of type "' . $type . '" which does not match with "' . $search . '". Return false'); + + return false; + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/UserAction.php b/app/Rules/Triggers/UserAction.php index 48c9932a33..bdde712e36 100644 --- a/app/Rules/Triggers/UserAction.php +++ b/app/Rules/Triggers/UserAction.php @@ -48,6 +48,7 @@ class UserAction implements TriggerInterface public function triggered() { Log::debug('user_action always returns true.'); + return true; } diff --git a/config/firefly.php b/config/firefly.php index ef85e627f6..eb78cf5102 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -174,11 +174,11 @@ return [ 'from_account_ends' => 'FireflyIII\Rules\Triggers\FromAccountEnds', 'from_account_is' => 'FireflyIII\Rules\Triggers\FromAccountIs', 'from_account_contains' => 'FireflyIII\Rules\Triggers\FromAccountContains', - 'to_account_starts' => 'FireflyIII\Rules\Triggers', - 'to_account_ends' => 'FireflyIII\Rules\Triggers', - 'to_account_is' => 'FireflyIII\Rules\Triggers', - 'to_account_contains' => 'FireflyIII\Rules\Triggers', - 'transaction_type' => 'FireflyIII\Rules\Triggers', + 'to_account_starts' => 'FireflyIII\Rules\Triggers\ToAccountStarts', + 'to_account_ends' => 'FireflyIII\Rules\Triggers\ToAccountEnds', + 'to_account_is' => 'FireflyIII\Rules\Triggers\ToAccountIs', + 'to_account_contains' => 'FireflyIII\Rules\Triggers\ToAccountContains', + 'transaction_type' => 'FireflyIII\Rules\Triggers\TransactionType', 'amount_less' => 'FireflyIII\Rules\Triggers', 'amount_exactly' => 'FireflyIII\Rules\Triggers', 'amount_exactly_not' => 'FireflyIII\Rules\Triggers', From 1270e5d15ca30a756cc648af68317558ac7f6048 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:37:19 +0100 Subject: [PATCH 109/276] More valid triggers. --- app/Rules/Triggers/AmountExactly.php | 64 ++++++++++++++++++++ app/Rules/Triggers/AmountLess.php | 64 ++++++++++++++++++++ app/Rules/Triggers/AmountMore.php | 64 ++++++++++++++++++++ app/Rules/Triggers/DescriptionEnds.php | 74 ++++++++++++++++++++++++ app/Rules/Triggers/DescriptionStarts.php | 62 ++++++++++++++++++++ app/Rules/Triggers/ToAccountEnds.php | 2 +- config/firefly.php | 9 ++- 7 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 app/Rules/Triggers/AmountExactly.php create mode 100644 app/Rules/Triggers/AmountLess.php create mode 100644 app/Rules/Triggers/AmountMore.php create mode 100644 app/Rules/Triggers/DescriptionEnds.php create mode 100644 app/Rules/Triggers/DescriptionStarts.php diff --git a/app/Rules/Triggers/AmountExactly.php b/app/Rules/Triggers/AmountExactly.php new file mode 100644 index 0000000000..908ad31a6f --- /dev/null +++ b/app/Rules/Triggers/AmountExactly.php @@ -0,0 +1,64 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $amount = $this->journal->amount_positive; + $compare = $this->trigger->trigger_value; + $result = bccomp($amount, $compare, 4); + if ($result === 0) { + // found something + Log::debug($amount . ' is exactly ' . $compare . '. Return true.'); + + return true; + } + + // found nothing. + Log::debug($amount . ' is not exactly ' . $compare . '. Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/AmountLess.php b/app/Rules/Triggers/AmountLess.php new file mode 100644 index 0000000000..4c659db944 --- /dev/null +++ b/app/Rules/Triggers/AmountLess.php @@ -0,0 +1,64 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $amount = $this->journal->amount_positive; + $compare = $this->trigger->trigger_value; + $result = bccomp($amount, $compare, 4); + if ($result === -1) { + // found something + Log::debug($amount . ' is less than ' . $compare . '. Return true.'); + + return true; + } + + // found nothing. + Log::debug($amount . ' is not less than ' . $compare . '. Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/AmountMore.php b/app/Rules/Triggers/AmountMore.php new file mode 100644 index 0000000000..a3470ffd13 --- /dev/null +++ b/app/Rules/Triggers/AmountMore.php @@ -0,0 +1,64 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $amount = $this->journal->amount_positive; + $compare = $this->trigger->trigger_value; + $result = bccomp($amount, $compare, 4); + if ($result === 1) { + // found something + Log::debug($amount . ' is more than ' . $compare . '. Return true.'); + + return true; + } + + // found nothing. + Log::debug($amount . ' is not more than ' . $compare . '. Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/DescriptionEnds.php b/app/Rules/Triggers/DescriptionEnds.php new file mode 100644 index 0000000000..4d7c0a10a4 --- /dev/null +++ b/app/Rules/Triggers/DescriptionEnds.php @@ -0,0 +1,74 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $description = strtolower($this->journal->description); + $descriptionLength = strlen($description); + $search = strtolower($this->trigger->trigger_value); + $searchLength = strlen($search); + + // if the string to search for is longer than the account name, + // shorten the search string. + if ($searchLength > $descriptionLength) { + Log::debug('Search string "' . $search . '" (' . $searchLength . ') is longer than "' . $description . '" (' . $descriptionLength . '). '); + $search = substr($search, ($descriptionLength * -1)); + $searchLength = strlen($search); + Log::debug('Search string is now "' . $search . '" (' . $searchLength . ') instead.'); + } + + + $part = substr($description, $searchLength * -1); + + if ($part == $search) { + Log::debug('"' . $description . '" ends with "' . $search . '". Return true.'); + + return true; + } + Log::debug('"' . $description . '" does not end with "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/DescriptionStarts.php b/app/Rules/Triggers/DescriptionStarts.php new file mode 100644 index 0000000000..8475f99fda --- /dev/null +++ b/app/Rules/Triggers/DescriptionStarts.php @@ -0,0 +1,62 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $description = strtolower($this->journal->description); + $search = strtolower($this->trigger->trigger_value); + + $part = substr($description, 0, strlen($search)); + + if ($part == $search) { + Log::debug('"' . $description . '" starts with "' . $search . '". Return true.'); + + return true; + } + Log::debug('"' . $description . '" does not start with "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file diff --git a/app/Rules/Triggers/ToAccountEnds.php b/app/Rules/Triggers/ToAccountEnds.php index 67bb9750f1..7c1984ea2a 100644 --- a/app/Rules/Triggers/ToAccountEnds.php +++ b/app/Rules/Triggers/ToAccountEnds.php @@ -18,7 +18,7 @@ use Log; * * @package FireflyIII\Rules\Triggers */ -class Ends implements TriggerInterface +class ToAccountEnds implements TriggerInterface { /** @var RuleTrigger */ protected $trigger; diff --git a/config/firefly.php b/config/firefly.php index eb78cf5102..4acb99dfd8 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -179,11 +179,10 @@ return [ 'to_account_is' => 'FireflyIII\Rules\Triggers\ToAccountIs', 'to_account_contains' => 'FireflyIII\Rules\Triggers\ToAccountContains', 'transaction_type' => 'FireflyIII\Rules\Triggers\TransactionType', - 'amount_less' => 'FireflyIII\Rules\Triggers', - 'amount_exactly' => 'FireflyIII\Rules\Triggers', - 'amount_exactly_not' => 'FireflyIII\Rules\Triggers', - 'amount_more' => 'FireflyIII\Rules\Triggers', - 'description_starts' => 'FireflyIII\Rules\Triggers', + 'amount_less' => 'FireflyIII\Rules\Triggers\AmountLess', + 'amount_exactly' => 'FireflyIII\Rules\Triggers\AmountExactly', + 'amount_more' => 'FireflyIII\Rules\Triggers\AmountMore', + 'description_starts' => 'FireflyIII\Rules\Triggers\DescriptionStarts', 'description_ends' => 'FireflyIII\Rules\Triggers', 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', 'description_is' => 'FireflyIII\Rules\Triggers', From 3dbb1f034db93ddf8c21ff9b36cc4cc4ef0ca3f7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 14:51:49 +0100 Subject: [PATCH 110/276] Fix the last triggers. --- app/Rules/Triggers/DescriptionEnds.php | 2 +- app/Rules/Triggers/DescriptionIs.php | 60 ++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 app/Rules/Triggers/DescriptionIs.php diff --git a/app/Rules/Triggers/DescriptionEnds.php b/app/Rules/Triggers/DescriptionEnds.php index 4d7c0a10a4..ef21b8aa4c 100644 --- a/app/Rules/Triggers/DescriptionEnds.php +++ b/app/Rules/Triggers/DescriptionEnds.php @@ -49,7 +49,7 @@ class DescriptionEnds implements TriggerInterface $search = strtolower($this->trigger->trigger_value); $searchLength = strlen($search); - // if the string to search for is longer than the account name, + // if the string to search for is longer than the description, // shorten the search string. if ($searchLength > $descriptionLength) { Log::debug('Search string "' . $search . '" (' . $searchLength . ') is longer than "' . $description . '" (' . $descriptionLength . '). '); diff --git a/app/Rules/Triggers/DescriptionIs.php b/app/Rules/Triggers/DescriptionIs.php new file mode 100644 index 0000000000..f7199f0103 --- /dev/null +++ b/app/Rules/Triggers/DescriptionIs.php @@ -0,0 +1,60 @@ +trigger = $trigger; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function triggered() + { + $description = strtolower($this->journal->description); + $search = strtolower($this->trigger->trigger_value); + + if ($description == $search) { + Log::debug('"' . $description . '" equals "' . $search . '" exactly. Return true.'); + + return true; + } + Log::debug('"' . $description . '" does not equal "' . $search . '". Return false.'); + + return false; + + } +} \ No newline at end of file From 668633e7642a534a89cb298588fd60bf54cb2485 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 15:10:49 +0100 Subject: [PATCH 111/276] All actions and triggers. --- app/Rules/Actions/AddTag.php | 56 ++++++++++++++++++++++ app/Rules/Actions/AppendDescription.php | 48 +++++++++++++++++++ app/Rules/Actions/ClearBudget.php | 51 ++++++++++++++++++++ app/Rules/Actions/ClearCategory.php | 51 ++++++++++++++++++++ app/Rules/Actions/PrependDescription.php | 48 +++++++++++++++++++ app/Rules/Actions/RemoveAllTags.php | 48 +++++++++++++++++++ app/Rules/Actions/RemoveTag.php | 61 ++++++++++++++++++++++++ app/Rules/Actions/SetDescription.php | 48 +++++++++++++++++++ config/firefly.php | 20 ++++---- 9 files changed, 421 insertions(+), 10 deletions(-) create mode 100644 app/Rules/Actions/AddTag.php create mode 100644 app/Rules/Actions/AppendDescription.php create mode 100644 app/Rules/Actions/ClearBudget.php create mode 100644 app/Rules/Actions/ClearCategory.php create mode 100644 app/Rules/Actions/PrependDescription.php create mode 100644 app/Rules/Actions/RemoveAllTags.php create mode 100644 app/Rules/Actions/RemoveTag.php create mode 100644 app/Rules/Actions/SetDescription.php diff --git a/app/Rules/Actions/AddTag.php b/app/Rules/Actions/AddTag.php new file mode 100644 index 0000000000..147d79a2df --- /dev/null +++ b/app/Rules/Actions/AddTag.php @@ -0,0 +1,56 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + // journal has this tag maybe? + $tag = Tag::firstOrCreateEncrypted(['tag' => $this->action->action_value, 'user_id' => Auth::user()->id]); + + $count = $this->journal->tags()->where('id', $tag->id)->count(); + if ($count == 0) { + $this->journal->tags()->save($tag); + } + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/AppendDescription.php b/app/Rules/Actions/AppendDescription.php new file mode 100644 index 0000000000..42254dc86b --- /dev/null +++ b/app/Rules/Actions/AppendDescription.php @@ -0,0 +1,48 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $this->journal->description = $this->journal->description . $this->action->action_value; + $this->journal->save(); + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/ClearBudget.php b/app/Rules/Actions/ClearBudget.php new file mode 100644 index 0000000000..f74b633537 --- /dev/null +++ b/app/Rules/Actions/ClearBudget.php @@ -0,0 +1,51 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $this->journal->budgets()->detach(); + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/ClearCategory.php b/app/Rules/Actions/ClearCategory.php new file mode 100644 index 0000000000..107f2241e0 --- /dev/null +++ b/app/Rules/Actions/ClearCategory.php @@ -0,0 +1,51 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $this->journal->categories()->detach(); + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/PrependDescription.php b/app/Rules/Actions/PrependDescription.php new file mode 100644 index 0000000000..d452125002 --- /dev/null +++ b/app/Rules/Actions/PrependDescription.php @@ -0,0 +1,48 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $this->journal->description = $this->action->action_value . $this->journal->description; + $this->journal->save(); + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/RemoveAllTags.php b/app/Rules/Actions/RemoveAllTags.php new file mode 100644 index 0000000000..868acee3cd --- /dev/null +++ b/app/Rules/Actions/RemoveAllTags.php @@ -0,0 +1,48 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $this->journal->tags()->detach(); + + return true; + + } +} \ No newline at end of file diff --git a/app/Rules/Actions/RemoveTag.php b/app/Rules/Actions/RemoveTag.php new file mode 100644 index 0000000000..e6c427d0bb --- /dev/null +++ b/app/Rules/Actions/RemoveTag.php @@ -0,0 +1,61 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + // if tag does not exist, no need to continue: + $name = $this->action->action_value; + /** @var Tag $tag */ + $tag = Auth::user()->tags()->get()->filter( + function (Tag $tag) use ($name) { + return $tag->tag == $name; + } + )->first(); + + if (!is_null($tag)) { + $this->journal->tags()->detach([$tag->id]); + } + + return true; + } +} \ No newline at end of file diff --git a/app/Rules/Actions/SetDescription.php b/app/Rules/Actions/SetDescription.php new file mode 100644 index 0000000000..1b3ee0422f --- /dev/null +++ b/app/Rules/Actions/SetDescription.php @@ -0,0 +1,48 @@ +action = $action; + $this->journal = $journal; + } + + /** + * @return bool + */ + public function act() + { + $this->journal->description = $this->action->action_value; + $this->journal->save(); + + return true; + } +} \ No newline at end of file diff --git a/config/firefly.php b/config/firefly.php index 4acb99dfd8..8a01676e8a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -183,21 +183,21 @@ return [ 'amount_exactly' => 'FireflyIII\Rules\Triggers\AmountExactly', 'amount_more' => 'FireflyIII\Rules\Triggers\AmountMore', 'description_starts' => 'FireflyIII\Rules\Triggers\DescriptionStarts', - 'description_ends' => 'FireflyIII\Rules\Triggers', + 'description_ends' => 'FireflyIII\Rules\Triggers\DescriptionEnds', 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', - 'description_is' => 'FireflyIII\Rules\Triggers', + 'description_is' => 'FireflyIII\Rules\Triggers\DescriptionIs', ], 'rule-actions' => [ 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', - 'clear_category' => 'FireflyIII\Rules\Actions', + 'clear_category' => 'FireflyIII\Rules\Actions\ClearCategory', 'set_budget' => 'FireflyIII\Rules\Actions\SetBudget', - 'clear_budget' => 'FireflyIII\Rules\Actions', - 'add_tag' => 'FireflyIII\Rules\Actions', - 'remove_tag' => 'FireflyIII\Rules\Actions', - 'remove_all_tags' => 'FireflyIII\Rules\Actions', - 'set_description' => 'FireflyIII\Rules\Actions', - 'append_description' => 'FireflyIII\Rules\Actions', - 'prepend_description' => 'FireflyIII\Rules\Actions', + 'clear_budget' => 'FireflyIII\Rules\Actions\ClearBudget', + 'add_tag' => 'FireflyIII\Rules\Actions\AddTag', + 'remove_tag' => 'FireflyIII\Rules\Actions\RemoveTag', + 'remove_all_tags' => 'FireflyIII\Rules\Actions\RemoveAllTags', + 'set_description' => 'FireflyIII\Rules\Actions\SetDescription', + 'append_description' => 'FireflyIII\Rules\Actions\AppendDescription', + 'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription', ], ]; From 768508dd4b5fe1c09bc30c417cdc10f460eb9b2e Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 15:59:45 +0100 Subject: [PATCH 112/276] First attempt at interface. --- app/Http/Controllers/RuleController.php | 5 +- app/Http/routes.php | 25 +++---- resources/views/rules/index.twig | 89 ++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index d99ab1f4f6..3c7ce3f31e 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -9,6 +9,7 @@ namespace FireflyIII\Http\Controllers; +use Auth; use FireflyIII\Http\Requests; use View; @@ -35,6 +36,8 @@ class RuleController extends Controller */ public function index() { - return view('rules.index'); + $ruleGroups = Auth::user()->ruleGroups()->with('rules')->get(); + + return view('rules.index', compact('ruleGroups')); } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 8b164a8ec8..1651ee9e1c 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,16 +1,4 @@ 'RuleController@index', 'as' => 'rules.index']); + // rules: + Route::get('/rules/rules/create', ['uses' => 'RuleController@createRule', 'as' => 'rules.rule.create']); + Route::get('/rules/rules/up/{rule}', ['uses' => 'RuleController@upRule', 'as' => 'rules.rule.up']); + Route::get('/rules/rules/down/{rule}', ['uses' => 'RuleController@downRule', 'as' => 'rules.rule.down']); + Route::get('/rules/rules/edit/{rule}', ['uses' => 'RuleController@editRule', 'as' => 'rules.rule.edit']); + Route::get('/rules/rules/delete/{rule}', ['uses' => 'RuleController@deleteRule', 'as' => 'rules.rule.delete']); + + // rule groups: + Route::get('/rules/groups/create', ['uses' => 'RuleController@createRuleGroup', 'as' => 'rules.rule-group.create']); + Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleController@editRuleGroup', 'as' => 'rules.rule-group.edit']); + Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); + + /** * Search Controller */ diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 9a934d37b3..9ff1e870bd 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -6,15 +6,100 @@
    -
    +

    {{ 'rules'|_ }}

    + +
    + +
    - Bla bla bla +

    + {{ 'rules_explanation'|_ }} +

    + {% for ruleGroup in ruleGroups %} +
    +
    +
    +
    +

    {{ ruleGroup.title }}

    + + + + +
    +
    +

    + {{ ruleGroup.description }} +

    + + {% if ruleGroup.rules.count > 0 %} + + + + + + + + + + {% for rule in ruleGroup.rules %} + + + + + + + {% endfor %} + +
    {{ 'rule_name'|_ }}{{ 'rule_triggers'|_ }}{{ 'rule_actions'|_ }}
    +
    + + + + +
    + +
    {{ rule.title }} + {% if rule.description != "" %} +
    {{ rule.description }}
    + {% endif %} +
    XY
    + {% else %} +

    + {{ 'no_rules_in_group'|_ }} +

    + {% endif %} +

    +
    + {{ 'new_rule'|_ }} +

    +
    +
    +
    +
    + {% endfor %} + + + {% endblock %} From 5ac88623ed9135887c655705128369de6d2b147b Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 16:05:39 +0100 Subject: [PATCH 113/276] Prep stuff for routes and actions. --- app/Http/Controllers/RuleController.php | 9 +++++++++ app/Models/Rule.php | 18 +++++++++++++++++- config/firefly.php | 2 ++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 3c7ce3f31e..433c637332 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -11,6 +11,7 @@ namespace FireflyIII\Http\Controllers; use Auth; use FireflyIII\Http\Requests; +use FireflyIII\Models\Rule; use View; /** @@ -40,4 +41,12 @@ class RuleController extends Controller return view('rules.index', compact('ruleGroups')); } + + /** + * @param Rule $rule + */ + public function upRule(Rule $rule) + { + + } } diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 7919401c5e..ca438e3224 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -9,8 +9,9 @@ namespace FireflyIII\Models; -use Illuminate\Database\Eloquent\Builder; +use Auth; use Illuminate\Database\Eloquent\Model; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Rule @@ -66,4 +67,19 @@ class Rule extends Model return $this->hasMany('FireflyIII\Models\RuleTrigger'); } + /** + * @param Rule $value + * + * @return Rule + */ + public static function routeBinder(Rule $value) + { + if (Auth::check()) { + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + } diff --git a/config/firefly.php b/config/firefly.php index 8a01676e8a..ddef1a0805 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -158,6 +158,8 @@ return [ 'piggyBank' => 'FireflyIII\Models\PiggyBank', 'tj' => 'FireflyIII\Models\TransactionJournal', 'tag' => 'FireflyIII\Models\Tag', + 'rule' => 'FireflyIII\Models\Rule', + 'ruleGroup' => 'FireflyIII\Models\RuleGroup', // lists 'accountList' => 'FireflyIII\Support\Binder\AccountList', 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', From 20941dedd394d004340be2368a7667d5785446ce Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 16:08:05 +0100 Subject: [PATCH 114/276] Also fix rule group. --- app/Http/Controllers/RuleController.php | 8 ++++++ app/Models/RuleGroup.php | 37 ++++++++++++++++++------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 433c637332..09a69ecfe6 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -12,6 +12,7 @@ namespace FireflyIII\Http\Controllers; use Auth; use FireflyIII\Http\Requests; use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleGroup; use View; /** @@ -49,4 +50,11 @@ class RuleController extends Controller { } + + /** + * @param RuleGroup $ruleGroup + */ + public function editRuleGroup(RuleGroup $ruleGroup) { + + } } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 894a88e748..4814eed163 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -9,22 +9,24 @@ namespace FireflyIII\Models; +use Auth; use Illuminate\Database\Eloquent\Model; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class RuleGroup * * @package FireflyIII\Models - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $user_id - * @property integer $order - * @property string $title - * @property string $description - * @property boolean $active - * @property-read \FireflyIII\User $user + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property integer $user_id + * @property integer $order + * @property string $title + * @property string $description + * @property boolean $active + * @property-read \FireflyIII\User $user * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Rule[] $rules */ class RuleGroup extends Model @@ -45,4 +47,19 @@ class RuleGroup extends Model { return $this->hasMany('FireflyIII\Models\Rule'); } + + /** + * @param RuleGroup $value + * + * @return Rule + */ + public static function routeBinder(RuleGroup $value) + { + if (Auth::check()) { + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } } From e9ee93beb76b97134bb48cdf3c94bb34f5756812 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 16:51:55 +0100 Subject: [PATCH 115/276] Expand view and translations. --- resources/lang/en_US/firefly.php | 110 +++++++++++++++++++++---------- resources/views/rules/index.twig | 48 ++++++++++---- 2 files changed, 109 insertions(+), 49 deletions(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ecdbe1b167..f903077de7 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -2,44 +2,82 @@ return [ // general stuff: - 'language_incomplete' => 'This language is not yet fully translated', - 'test' => 'You have selected English.', - 'close' => 'Close', - 'pleaseHold' => 'Please hold...', - 'actions' => 'Actions', - 'edit' => 'Edit', - 'delete' => 'Delete', - 'welcomeBack' => 'What\'s playing?', - 'everything' => 'Everything', - 'customRange' => 'Custom range', - 'apply' => 'Apply', - 'cancel' => 'Cancel', - 'from' => 'From', - 'to' => 'To', - 'total_sum' => 'Total sum', - 'period_sum' => 'Sum for period', - 'showEverything' => 'Show everything', - 'never' => 'Never', - 'search_results_for' => 'Search results for ":query"', - 'bounced_error' => 'The message sent to :email bounced, so no access for you.', - 'deleted_error' => 'These credentials do not match our records.', - 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', - 'removed_amount' => 'Removed :amount', - 'added_amount' => 'Added :amount', - 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', - 'Opening balance' => 'Opening balance', - 'create_new_stuff' => 'Create new stuff', - 'new_withdrawal' => 'New withdrawal', - 'new_deposit' => 'New deposit', - 'new_transfer' => 'New transfer', - 'new_asset_account' => 'New asset account', - 'new_expense_account' => 'New expense account', - 'new_revenue_account' => 'New revenue account', - 'new_budget' => 'New budget', - 'new_bill' => 'New bill', + 'language_incomplete' => 'This language is not yet fully translated', + 'test' => 'You have selected English.', + 'close' => 'Close', + 'pleaseHold' => 'Please hold...', + 'actions' => 'Actions', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'welcomeBack' => 'What\'s playing?', + 'everything' => 'Everything', + 'customRange' => 'Custom range', + 'apply' => 'Apply', + 'cancel' => 'Cancel', + 'from' => 'From', + 'to' => 'To', + 'total_sum' => 'Total sum', + 'period_sum' => 'Sum for period', + 'showEverything' => 'Show everything', + 'never' => 'Never', + 'search_results_for' => 'Search results for ":query"', + 'bounced_error' => 'The message sent to :email bounced, so no access for you.', + 'deleted_error' => 'These credentials do not match our records.', + 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', + 'removed_amount' => 'Removed :amount', + 'added_amount' => 'Added :amount', + 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'Opening balance' => 'Opening balance', + 'create_new_stuff' => 'Create new stuff', + 'new_withdrawal' => 'New withdrawal', + 'new_deposit' => 'New deposit', + 'new_transfer' => 'New transfer', + 'new_asset_account' => 'New asset account', + 'new_expense_account' => 'New expense account', + 'new_revenue_account' => 'New revenue account', + 'new_budget' => 'New budget', + 'new_bill' => 'New bill', // rules - 'rules' => 'Rules', + 'rules' => 'Rules', + 'rules_explanation' => 'Here is going to be text', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + + // actions and triggers + 'rule_trigger_user_action' => '', + 'rule_trigger_from_account_starts' => 'from_account_starts', + 'rule_trigger_from_account_ends' => 'from_account_ends', + 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', + 'rule_trigger_from_account_contains' => '', + 'rule_trigger_to_account_starts' => '', + 'rule_trigger_to_account_ends' => '', + 'rule_trigger_to_account_is' => '', + 'rule_trigger_to_account_contains' => '', + 'rule_trigger_transaction_type' => '', + 'rule_trigger_amount_less' => '', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more' => '', + 'rule_trigger_description_starts' => '', + 'rule_trigger_description_ends' => '', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is' => '', + + 'rule_action_set_category' => 'Set category to ":action_value"', + 'rule_action_clear_category' => 'Clear category', + 'rule_action_set_budget' => 'Set budget to ":action_value"', + 'rule_action_clear_budget' => 'Clear budget', + 'rule_action_add_tag' => 'Add tag ":action_value"', + 'rule_action_remove_tag' => 'Remove tag ":action_value"', + 'rule_action_remove_all_tags' => 'Remove all tags', + 'rule_action_set_description' => 'Set description to ":action_value"', + 'rule_action_append_description' => 'Append description with ":action_value"', + 'rule_action_prepend_description' => 'Prepend description with ":action_value"', // tags 'store_new_tag' => 'Store new tag', diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 9ff1e870bd..829b626fdd 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -50,23 +50,27 @@

    {% if ruleGroup.rules.count > 0 %} - +
    - - - - - + + + + + {% for rule in ruleGroup.rules %} @@ -75,8 +79,26 @@
    {{ rule.description }}
    {% endif %} - - + + {% endfor %} @@ -87,7 +109,7 @@

    {% endif %}

    -
    +
    {{ 'new_rule'|_ }}

    From 27aae279e647eeefbe4c01caebddb1fd25aacccc Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 18:34:56 +0100 Subject: [PATCH 116/276] More code for rules. --- app/Http/Controllers/RuleController.php | 57 ++++++++++++++++++- app/Http/Requests/RuleGroupFormRequest.php | 49 ++++++++++++++++ app/Http/routes.php | 2 + app/Models/RuleGroup.php | 3 + app/Providers/FireflyServiceProvider.php | 1 + app/Repositories/Rule/RuleRepository.php | 56 ++++++++++++++++++ .../Rule/RuleRepositoryInterface.php | 33 +++++++++++ resources/lang/en_US/firefly.php | 4 ++ resources/views/rules/create-rule-group.twig | 51 +++++++++++++++++ 9 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 app/Http/Requests/RuleGroupFormRequest.php create mode 100644 app/Repositories/Rule/RuleRepository.php create mode 100644 app/Repositories/Rule/RuleRepositoryInterface.php create mode 100644 resources/views/rules/create-rule-group.twig diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 09a69ecfe6..728891eaa0 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -11,8 +11,14 @@ namespace FireflyIII\Http\Controllers; use Auth; use FireflyIII\Http\Requests; +use FireflyIII\Http\Requests\RuleGroupFormRequest; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use Input; +use Preferences; +use Session; +use URL; use View; /** @@ -32,6 +38,54 @@ class RuleController extends Controller View::share('mainTitleIcon', 'fa-random'); } + /** + * @return View + */ + public function createRuleGroup() + { + $subTitleIcon = 'fa-clone'; + $subTitle = trans('firefly.make_new_rule_group'); + + // put previous url in session if not redirect from store (not "create another"). + if (Session::get('rule-groups.create.fromStore') !== true) { + Session::put('rule-groups.create.url', URL::previous()); + } + Session::forget('accounts.create.fromStore'); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'create-rule-group'); + + return view('rules.create-rule-group', compact('subTitleIcon', 'what', 'subTitle')); + } + + /** + * @param RuleGroupFormRequest $request + * @param RuleRepositoryInterface $repository + * + * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function storeRuleGroup(RuleGroupFormRequest $request, RuleRepositoryInterface $repository) + { + $data = [ + 'title' => $request->input('title'), + 'description' => $request->input('description'), + 'user' => Auth::user()->id, + ]; + + $ruleGroup = $repository->storeRuleGroup($data); + + Session::flash('success', trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])); + Preferences::mark(); + + if (intval(Input::get('create_another')) === 1) { + // set value so create routine will not overwrite URL: + Session::put('rule-groups.create.fromStore', true); + + return redirect(route('rules.rule-group.create'))->withInput(); + } + + // redirect to previous URL. + return redirect(Session::get('rule-groups.create.url')); + } /** * @return View @@ -54,7 +108,8 @@ class RuleController extends Controller /** * @param RuleGroup $ruleGroup */ - public function editRuleGroup(RuleGroup $ruleGroup) { + public function editRuleGroup(RuleGroup $ruleGroup) + { } } diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php new file mode 100644 index 0000000000..638c50341b --- /dev/null +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -0,0 +1,49 @@ + $titleRule, + 'description' => 'between:1,5000', + ]; + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 1651ee9e1c..d567211762 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -247,6 +247,8 @@ Route::group( Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleController@editRuleGroup', 'as' => 'rules.rule-group.edit']); Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); + Route::post('/rules/groups/store', ['uses' => 'RuleController@storeRuleGroup', 'as' => 'rules.rule-group.store']); + /** * Search Controller diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 4814eed163..e9a9793f33 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -31,6 +31,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class RuleGroup extends Model { + + protected $fillable = ['user_id', 'order', 'title', 'description', 'active']; + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 81df15ccac..7ca97848d9 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -90,6 +90,7 @@ class FireflyServiceProvider extends ServiceProvider $this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository'); $this->app->bind('FireflyIII\Repositories\Tag\TagRepositoryInterface', 'FireflyIII\Repositories\Tag\TagRepository'); $this->app->bind('FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface', 'FireflyIII\Repositories\Attachment\AttachmentRepository'); + $this->app->bind('FireflyIII\Repositories\Rule\RuleRepositoryInterface', 'FireflyIII\Repositories\Rule\RuleRepository'); $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search'); // CSV import diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php new file mode 100644 index 0000000000..1735039246 --- /dev/null +++ b/app/Repositories/Rule/RuleRepository.php @@ -0,0 +1,56 @@ +ruleGroups()->max('order'); + + return intval($entry); + } + + /** + * @param array $data + * + * @return RuleGroup + */ + public function storeRuleGroup(array $data) + { + $order = $this->getHighestOrderRuleGroup(); + + $newRuleGroup = new RuleGroup( + [ + 'user_id' => $data['user'], + 'title' => $data['title'], + 'description' => $data['description'], + 'order' => ($order + 1), + 'active' => 1, + + + ] + ); + $newRuleGroup->save(); + + return $newRuleGroup; + } +} \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php new file mode 100644 index 0000000000..1130b49f2f --- /dev/null +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -0,0 +1,33 @@ + 'New rule group', 'rule_priority_up' => 'Give rule more priority', 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'no_rules_in_group' => 'There are no rules in this group', // actions and triggers 'rule_trigger_user_action' => '', diff --git a/resources/views/rules/create-rule-group.twig b/resources/views/rules/create-rule-group.twig new file mode 100644 index 0000000000..4bff91f4f3 --- /dev/null +++ b/resources/views/rules/create-rule-group.twig @@ -0,0 +1,51 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + {{ Form.open({'class' : 'form-horizontal','id' : 'store','url' : route('rules.rule-group.store')}) }} +
    +
    +
    +
    +

    {{ 'mandatoryFields'|_ }}

    +
    +
    + {{ ExpandedForm.text('title') }} +
    +
    +
    +
    + + +
    +
    +

    {{ 'optionalFields'|_ }}

    +
    +
    + {{ ExpandedForm.textarea('description') }} +
    +
    + + +
    +
    +

    {{ 'options'|_ }}

    +
    +
    + {{ ExpandedForm.optionsList('create','rule-group') }} +
    + +
    + +
    + +
    + {{ Form.close|raw }} + + +{% endblock %} From b2bb16c1e062cee1a23467065c71f03940d35dbc Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 18:35:09 +0100 Subject: [PATCH 117/276] Updated composer file. --- composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index b48e815f45..ac61669c9c 100644 --- a/composer.lock +++ b/composer.lock @@ -759,16 +759,16 @@ }, { "name": "laravel/framework", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "26cd65eaa4bcc0fb0be381cfb7cfdcda06a3c2b4" + "reference": "867914788c2ec942967c3608ed54626228a5f916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/26cd65eaa4bcc0fb0be381cfb7cfdcda06a3c2b4", - "reference": "26cd65eaa4bcc0fb0be381cfb7cfdcda06a3c2b4", + "url": "https://api.github.com/repos/laravel/framework/zipball/867914788c2ec942967c3608ed54626228a5f916", + "reference": "867914788c2ec942967c3608ed54626228a5f916", "shasum": "" }, "require": { @@ -883,7 +883,7 @@ "framework", "laravel" ], - "time": "2016-01-07 13:54:34" + "time": "2016-01-12 22:45:00" }, { "name": "laravelcollective/html", @@ -2419,16 +2419,16 @@ }, { "name": "twig/twig", - "version": "v1.23.1", + "version": "v1.23.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" + "reference": "ae53fc2c312fdee63773b75cb570304f85388b08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", - "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ae53fc2c312fdee63773b75cb570304f85388b08", + "reference": "ae53fc2c312fdee63773b75cb570304f85388b08", "shasum": "" }, "require": { @@ -2476,7 +2476,7 @@ "keywords": [ "templating" ], - "time": "2015-11-05 12:49:06" + "time": "2016-01-11 14:02:19" }, { "name": "vlucas/phpdotenv", From 06174d6afb284923a862ea7f4bb0ccd6f4428bd9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 18:50:15 +0100 Subject: [PATCH 118/276] Edit rule group. --- app/Http/Controllers/RuleController.php | 72 +++++++++++++++---- app/Http/routes.php | 2 +- app/Repositories/Rule/RuleRepository.php | 17 +++++ .../Rule/RuleRepositoryInterface.php | 9 +++ resources/lang/en_US/firefly.php | 3 + .../create.twig} | 0 resources/views/rules/rule-group/edit.twig | 52 ++++++++++++++ 7 files changed, 142 insertions(+), 13 deletions(-) rename resources/views/rules/{create-rule-group.twig => rule-group/create.twig} (100%) create mode 100644 resources/views/rules/rule-group/edit.twig diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 728891eaa0..cb4d63f2ce 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -47,14 +47,14 @@ class RuleController extends Controller $subTitle = trans('firefly.make_new_rule_group'); // put previous url in session if not redirect from store (not "create another"). - if (Session::get('rule-groups.create.fromStore') !== true) { - Session::put('rule-groups.create.url', URL::previous()); + if (Session::get('rules.rule-group.create.fromStore') !== true) { + Session::put('rules.rule-group.create.url', URL::previous()); } Session::forget('accounts.create.fromStore'); Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'create-rule-group'); - return view('rules.create-rule-group', compact('subTitleIcon', 'what', 'subTitle')); + return view('rules.rule-group.create', compact('subTitleIcon', 'what', 'subTitle')); } /** @@ -78,15 +78,70 @@ class RuleController extends Controller if (intval(Input::get('create_another')) === 1) { // set value so create routine will not overwrite URL: - Session::put('rule-groups.create.fromStore', true); + Session::put('rules.rule-group.create.fromStore', true); return redirect(route('rules.rule-group.create'))->withInput(); } // redirect to previous URL. - return redirect(Session::get('rule-groups.create.url')); + return redirect(Session::get('rules.rule-group.create.url')); } + + /** + * @param RuleGroup $ruleGroup + * + * @return View + */ + public function editRuleGroup(RuleGroup $ruleGroup) + { + $subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]); + + // put previous url in session if not redirect from store (not "return_to_edit"). + if (Session::get('rules.rule-group.edit.fromUpdate') !== true) { + Session::put('rules.rule-group.edit.url', URL::previous()); + } + Session::forget('rules.rule-group.edit.fromUpdate'); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'edit-rule-group'); + + return view('rules.rule-group.edit', compact('ruleGroup', 'subTitle')); + + } + + /** + * @param RuleGroupFormRequest $request + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * + * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function updateRuleGroup(RuleGroupFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $data = [ + 'title' => $request->input('title'), + 'description' => $request->input('description'), + 'active' => intval($request->input('active')) == 1, + ]; + + $repository->update($ruleGroup, $data); + + Session::flash('success', trans('firefly.updated_rule_group', ['title' => $ruleGroup->title])); + Preferences::mark(); + + if (intval(Input::get('return_to_edit')) === 1) { + // set value so edit routine will not overwrite URL: + Session::put('rules.rule-group.edit.fromUpdate', true); + + return redirect(route('rules.rule-group.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]); + } + + // redirect to previous URL. + return redirect(Session::get('rules.rule-group.edit.url')); + + } + + /** * @return View */ @@ -105,11 +160,4 @@ class RuleController extends Controller } - /** - * @param RuleGroup $ruleGroup - */ - public function editRuleGroup(RuleGroup $ruleGroup) - { - - } } diff --git a/app/Http/routes.php b/app/Http/routes.php index d567211762..a454ddbf92 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -248,7 +248,7 @@ Route::group( Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); Route::post('/rules/groups/store', ['uses' => 'RuleController@storeRuleGroup', 'as' => 'rules.rule-group.store']); - + Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleController@updateRuleGroup', 'as' => 'rules.rule-group.update']); /** * Search Controller diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 1735039246..9dba43eaf7 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -53,4 +53,21 @@ class RuleRepository implements RuleRepositoryInterface return $newRuleGroup; } + + /** + * @param RuleGroup $ruleGroup + * @param array $data + * + * @return RuleGroup + */ + public function update(RuleGroup $ruleGroup, array $data) + { + // update the account: + $ruleGroup->title = $data['title']; + $ruleGroup->description = $data['description']; + $ruleGroup->active = $data['active']; + $ruleGroup->save(); + + return $ruleGroup; + } } \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 1130b49f2f..e1b910cef2 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -30,4 +30,13 @@ interface RuleRepositoryInterface */ public function getHighestOrderRuleGroup(); + + /** + * @param RuleGroup $ruleGroup + * @param array $data + * + * @return RuleGroup + */ + public function update(RuleGroup $ruleGroup, array $data); + } \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index e530067696..90d7bcd70b 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -51,6 +51,9 @@ return [ 'make_new_rule_group' => 'Make new rule group', 'store_new_rule_group' => 'Store new rule group', 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'update_rule_group' => 'Update rule group', 'no_rules_in_group' => 'There are no rules in this group', // actions and triggers diff --git a/resources/views/rules/create-rule-group.twig b/resources/views/rules/rule-group/create.twig similarity index 100% rename from resources/views/rules/create-rule-group.twig rename to resources/views/rules/rule-group/create.twig diff --git a/resources/views/rules/rule-group/edit.twig b/resources/views/rules/rule-group/edit.twig new file mode 100644 index 0000000000..a589c65715 --- /dev/null +++ b/resources/views/rules/rule-group/edit.twig @@ -0,0 +1,52 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} +{% endblock %} + +{% block content %} + {{ Form.model(ruleGroup, {'class' : 'form-horizontal','id' : 'update','url' : route('rules.rule-group.update',ruleGroup.id) } ) }} + +
    +
    +
    +
    +

    {{ 'mandatoryFields'|_ }}

    +
    +
    + {{ ExpandedForm.checkbox('active') }} + {{ ExpandedForm.text('title') }} +
    +
    + +
    +
    + + +
    +
    +

    {{ 'optionalFields'|_ }}

    +
    +
    + {{ ExpandedForm.textarea('description') }} +
    +
    + + +
    +
    +

    {{ 'options'|_ }}

    +
    +
    + {{ ExpandedForm.optionsList('update','budget') }} +
    + +
    +
    +
    + + {{ Form.close|raw }} + +{% endblock %} From 4697fbdeef9c2db4d9dacb7323f88664764b2932 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Jan 2016 21:44:26 +0100 Subject: [PATCH 119/276] Routine to delete rules. Should include routine to move rules to other group. --- app/Http/Controllers/RuleController.php | 39 +++++++++++++++- app/Http/routes.php | 1 + app/Models/Rule.php | 2 + app/Models/RuleGroup.php | 2 + app/Repositories/Rule/RuleRepository.php | 44 ++++++++++++++++++- .../Rule/RuleRepositoryInterface.php | 15 ++++++- .../2016_01_11_193428_changes_for_v370.php | 4 -- resources/lang/en_US/firefly.php | 2 + resources/lang/en_US/form.php | 3 ++ resources/views/rules/rule-group/delete.twig | 41 +++++++++++++++++ 10 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 resources/views/rules/rule-group/delete.twig diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index cb4d63f2ce..abe2bf9dc4 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -124,7 +124,7 @@ class RuleController extends Controller 'active' => intval($request->input('active')) == 1, ]; - $repository->update($ruleGroup, $data); + $repository->updateRuleGroup($ruleGroup, $data); Session::flash('success', trans('firefly.updated_rule_group', ['title' => $ruleGroup->title])); Preferences::mark(); @@ -141,6 +141,43 @@ class RuleController extends Controller } + /** + * @param RuleGroup $budget + * + * @return \Illuminate\View\View + */ + public function deleteRuleGroup(RuleGroup $ruleGroup) + { + $subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]); + + // put previous url in session + Session::put('rules.rule-group.delete.url', URL::previous()); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'delete-rule-group'); + + return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle')); + } + + /** + * @param RuleGroup $ruleGroup + * @param RuleRepositoryInterface $repository + * + * @return \Illuminate\Http\RedirectResponse + */ + public function destroyRuleGroup(RuleGroup $ruleGroup, RuleRepositoryInterface $repository) + { + + $title = $ruleGroup->title; + $repository->destroyRuleGroup($ruleGroup); + + + Session::flash('success', trans('firefly.deleted_rule_group', ['title' => $title])); + Preferences::mark(); + + + return redirect(Session::get('rules.rule-group.delete.url')); + } + /** * @return View diff --git a/app/Http/routes.php b/app/Http/routes.php index a454ddbf92..c6d261bd91 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -249,6 +249,7 @@ Route::group( Route::post('/rules/groups/store', ['uses' => 'RuleController@storeRuleGroup', 'as' => 'rules.rule-group.store']); Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleController@updateRuleGroup', 'as' => 'rules.rule-group.update']); + Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleController@destroyRuleGroup', 'as' => 'rules.rule-group.destroy']); /** * Search Controller diff --git a/app/Models/Rule.php b/app/Models/Rule.php index ca438e3224..b99d6334b0 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -11,6 +11,7 @@ namespace FireflyIII\Models; use Auth; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -35,6 +36,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class Rule extends Model { + use SoftDeletes; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index e9a9793f33..88c3733836 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -11,6 +11,7 @@ namespace FireflyIII\Models; use Auth; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -31,6 +32,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class RuleGroup extends Model { + use SoftDeletes; protected $fillable = ['user_id', 'order', 'title', 'description', 'active']; diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 9dba43eaf7..3a7bfb1c2d 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -10,6 +10,7 @@ namespace FireflyIII\Repositories\Rule; use Auth; +use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; /** @@ -50,6 +51,7 @@ class RuleRepository implements RuleRepositoryInterface ] ); $newRuleGroup->save(); + $this->resetRuleGroupOrder(); return $newRuleGroup; } @@ -60,14 +62,54 @@ class RuleRepository implements RuleRepositoryInterface * * @return RuleGroup */ - public function update(RuleGroup $ruleGroup, array $data) + public function updateRuleGroup(RuleGroup $ruleGroup, array $data) { // update the account: $ruleGroup->title = $data['title']; $ruleGroup->description = $data['description']; $ruleGroup->active = $data['active']; $ruleGroup->save(); + $this->resetRuleGroupOrder(); return $ruleGroup; } + + /** + * @param RuleGroup $ruleGroup + * + * @return boolean + */ + public function destroyRuleGroup(RuleGroup $ruleGroup) + { + /** @var Rule $rule */ + foreach ($ruleGroup->rules as $rule) { + $rule->delete(); + } + + $ruleGroup->delete(); + + $this->resetRuleGroupOrder(); + + return true; + } + + /** + * @return bool + */ + public function resetRuleGroupOrder() + { + Auth::user()->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]); + + $set = Auth::user()->ruleGroups()->where('active', 1)->orderBy('order', 'ASC')->get(); + $count = 1; + /** @var RuleGroup $entry */ + foreach ($set as $entry) { + $entry->order = $count; + $entry->save(); + $count++; + } + + + return true; + } } \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index e1b910cef2..ea4e2e1b5f 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -37,6 +37,19 @@ interface RuleRepositoryInterface * * @return RuleGroup */ - public function update(RuleGroup $ruleGroup, array $data); + public function updateRuleGroup(RuleGroup $ruleGroup, array $data); + + + /** + * @param RuleGroup $ruleGroup + * + * @return boolean + */ + public function destroyRuleGroup(RuleGroup $ruleGroup); + + /** + * @return bool + */ + public function resetRuleGroupOrder(); } \ No newline at end of file diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index adb0833a9f..20ba91e746 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -37,8 +37,6 @@ class ChangesForV370 extends Migration // connect rule groups to users $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - // order must be unique for rule group: - $table->unique(['user_id', 'order']); } ); @@ -65,8 +63,6 @@ class ChangesForV370 extends Migration // connect rules to rule groups $table->foreign('rule_group_id')->references('id')->on('rule_groups')->onDelete('cascade'); - // order must be unique for rules: - $table->unique(['user_id', 'order']); } ); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 90d7bcd70b..2e80cf5fc9 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -53,6 +53,8 @@ return [ 'created_new_rule_group' => 'New rule group ":title" stored!', 'updated_rule_group' => 'Successfully updated rule group ":title".', 'edit_rule_group' => 'Edit rule group ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', 'update_rule_group' => 'Update rule group', 'no_rules_in_group' => 'There are no rules in this group', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index b76eac684f..8b3b34f4c6 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -76,10 +76,12 @@ return [ 'delete_currency' => 'Delete currency ":name"', 'delete_journal' => 'Delete transaction with description ":description"', 'delete_attachment' => 'Delete attachment ":name"', + 'delete_rule_group' => 'Delete rule group ":title"', 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?', 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?', + 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titles ":title"?', 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', @@ -89,6 +91,7 @@ return [ 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', + 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.', 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', diff --git a/resources/views/rules/rule-group/delete.twig b/resources/views/rules/rule-group/delete.twig new file mode 100644 index 0000000000..7d5e8e1c1b --- /dev/null +++ b/resources/views/rules/rule-group/delete.twig @@ -0,0 +1,41 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} +{% endblock %} + +{% block content %} + {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('rules.rule-group.destroy',ruleGroup.id) }) }} +
    +
    +
    +
    +

    {{ trans('form.delete_rule_group', {'title': ruleGroup.title}) }}

    +
    +
    +

    + {{ trans('form.permDeleteWarning') }} +

    + +

    + {{ trans('form.ruleGroup_areYouSure', {'title': ruleGroup.title}) }} +

    + +

    + {% if ruleGroup.rules|length > 0 %} + {{ Lang.choice('form.also_delete_rules', ruleGroup.rules|length, {count: ruleGroup.rules|length}) }} + {% endif %} +

    + +
    + + + +
    +
    +
    + {{ Form.close|raw }} +{% endblock %} From 33899f0e2f955df95341e41dec5334625ccf46ef Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 09:08:44 +0100 Subject: [PATCH 120/276] New test data, new translations. --- .../2016_01_11_193428_changes_for_v370.php | 8 +- database/seeds/TestDataSeeder.php | 356 +++++++++++++++++- resources/lang/en_US/firefly.php | 28 +- 3 files changed, 355 insertions(+), 37 deletions(-) diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index 20ba91e746..263b39e7b6 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -31,7 +31,7 @@ class ChangesForV370 extends Migration $table->integer('user_id')->unsigned(); $table->unsignedSmallInteger('order'); $table->string('title', 255); - $table->text('description'); + $table->text('description')->nullable(); $table->unsignedTinyInteger('active')->default(1); // connect rule groups to users @@ -54,7 +54,7 @@ class ChangesForV370 extends Migration $table->unsignedTinyInteger('stop_processing')->default(0); $table->string('title', 255); - $table->text('description'); + $table->text('description')->nullable(); // connect rules to users @@ -78,7 +78,7 @@ class ChangesForV370 extends Migration $table->unsignedTinyInteger('stop_processing')->default(0); $table->string('trigger_type', 50); - $table->string('trigger_value', 255); + $table->string('trigger_value', 255)->nullable(); @@ -101,7 +101,7 @@ class ChangesForV370 extends Migration $table->unsignedTinyInteger('stop_processing')->default(0); $table->string('action_type', 50); - $table->string('action_value', 255); + $table->string('action_value', 255)->nullable(); diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index bb6ce4c74e..925f17048a 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -41,7 +41,7 @@ class TestDataSeeder extends Seeder public function createRules() { - // basic rules group. user should always have one + // three rule groups to get started. $ruleGroup = new RuleGroup; $ruleGroup->user()->associate($this->user); $ruleGroup->order = 1; @@ -49,19 +49,36 @@ class TestDataSeeder extends Seeder $ruleGroup->title = 'Default rules'; $ruleGroup->description = 'All your rules not in a particular group.'; $ruleGroup->save(); + unset($ruleGroup); + + $ruleGroup = new RuleGroup; + $ruleGroup->user()->associate($this->user); + $ruleGroup->order = 2; + $ruleGroup->active = 1; + $ruleGroup->title = 'Empty rule group'; + $ruleGroup->description = 'Intentionally has no rules.'; + $ruleGroup->save(); + unset($ruleGroup); - // a normal rule: saves transactions where description contains "groceries" - // and from account is "MyBank Checking Account" - // send it to Groceries/Groceries + $ruleGroup = new RuleGroup; + $ruleGroup->user()->associate($this->user); + $ruleGroup->order = 3; + $ruleGroup->active = 1; + $ruleGroup->title = 'Rules for bills'; + $ruleGroup->description = 'All rules for bills and recurring costs.'; + $ruleGroup->save(); + unset($ruleGroup); + + // move groceries to correct budget/category $rule = new Rule; $rule->user()->associate($this->user); - $rule->ruleGroup()->associate($ruleGroup); + $rule->ruleGroup()->associate(RuleGroup::find(1)); $rule->order = 1; $rule->active = 1; $rule->stop_processing = 0; - $rule->title = 'A strange rule for testing.'; - $rule->description = 'This rule triggers on transactions with the description "David Bowie" and puts them in the category "Blackstar".'; + $rule->title = 'Move groceries'; + $rule->description = 'Move groceries to correct category and budget.'; $rule->save(); @@ -108,8 +125,9 @@ class TestDataSeeder extends Seeder $ruleAction->active = 1; $ruleAction->stop_processing = 0; $ruleAction->action_type = 'set_category'; - $ruleAction->action_value = 'Automated Groceries'; + $ruleAction->action_value = 'Groceries'; $ruleAction->save(); + unset($ruleAction); // actions for this rule. one, set budget $ruleAction = new RuleAction; @@ -120,6 +138,306 @@ class TestDataSeeder extends Seeder $ruleAction->action_type = 'set_budget'; $ruleAction->action_value = 'Groceries'; $ruleAction->save(); + unset($ruleAction); + + + // move "gas" to "Car" and "Car" + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(1)); + $rule->order = 2; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'Move gas'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'gas'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'Car'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Car'; + $ruleAction->save(); + unset($ruleAction); + + // move savings to money management + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(1)); + $rule->order = 2; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'Move savings'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // is transfer + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'transaction_type'; + $ruleTrigger->trigger_value = 'Transfer'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_is'; + $ruleTrigger->trigger_value = 'Save money'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 4; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 5; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'to_account_is'; + $ruleTrigger->trigger_value = 'Savings'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'Money Management'; + $ruleAction->save(); + unset($ruleAction); + + // move TV bill to "Bills" and "House" + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(3)); + $rule->order = 1; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'TV Bill'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'tv bill'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'House'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Bills'; + $ruleAction->save(); + unset($ruleAction); + + // move rent to bills, rent. + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(3)); + $rule->order = 2; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'Rent'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'rent'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'House'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Bills'; + $ruleAction->save(); + unset($ruleAction); + + + // a normal rule: saves transactions where description contains "groceries" + // and from account is "MyBank Checking Account" + // send it to Groceries/Groceries //$ruleAction @@ -244,7 +562,7 @@ class TestDataSeeder extends Seeder [ 'accountRole' => 'ccAsset', 'ccMonthlyPaymentDate' => '2015-05-27', - 'ccType' => 'monthlyFull' + 'ccType' => 'monthlyFull', ], [ 'accountRole' => 'savingAsset', @@ -369,21 +687,21 @@ class TestDataSeeder extends Seeder [ 'piggy_bank_id' => $camera->id, 'date' => '2015-05-01', - 'amount' => '245' + 'amount' => '245', ] ); PiggyBankEvent::create( [ 'piggy_bank_id' => $camera->id, 'date' => '2015-06-01', - 'amount' => '245' + 'amount' => '245', ] ); PiggyBankEvent::create( [ 'piggy_bank_id' => $camera->id, 'date' => '2015-07-01', - 'amount' => '245' + 'amount' => '245', ] ); @@ -408,21 +726,21 @@ class TestDataSeeder extends Seeder [ 'piggy_bank_id' => $phone->id, 'date' => '2015-05-01', - 'amount' => '111' + 'amount' => '111', ] ); PiggyBankEvent::create( [ 'piggy_bank_id' => $phone->id, 'date' => '2015-06-01', - 'amount' => '111' + 'amount' => '111', ] ); PiggyBankEvent::create( [ 'piggy_bank_id' => $phone->id, 'date' => '2015-07-01', - 'amount' => '111' + 'amount' => '111', ] ); @@ -446,21 +764,21 @@ class TestDataSeeder extends Seeder [ 'piggy_bank_id' => $couch->id, 'date' => '2015-05-01', - 'amount' => '40' + 'amount' => '40', ] ); PiggyBankEvent::create( [ 'piggy_bank_id' => $couch->id, 'date' => '2015-06-01', - 'amount' => '40' + 'amount' => '40', ] ); PiggyBankEvent::create( [ 'piggy_bank_id' => $couch->id, 'date' => '2015-07-01', - 'amount' => '40' + 'amount' => '40', ] ); @@ -914,7 +1232,7 @@ class TestDataSeeder extends Seeder 'startdate' => $start->format('Y-m-d'), 'amount' => $amount, 'repeats' => 0, - 'repeat_freq' => 'monthly' + 'repeat_freq' => 'monthly', ] ); } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 2e80cf5fc9..fa33d0ace7 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -59,23 +59,23 @@ return [ 'no_rules_in_group' => 'There are no rules in this group', // actions and triggers - 'rule_trigger_user_action' => '', - 'rule_trigger_from_account_starts' => 'from_account_starts', - 'rule_trigger_from_account_ends' => 'from_account_ends', + 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', - 'rule_trigger_from_account_contains' => '', - 'rule_trigger_to_account_starts' => '', - 'rule_trigger_to_account_ends' => '', - 'rule_trigger_to_account_is' => '', - 'rule_trigger_to_account_contains' => '', - 'rule_trigger_transaction_type' => '', - 'rule_trigger_amount_less' => '', + 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', + 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', - 'rule_trigger_amount_more' => '', - 'rule_trigger_description_starts' => '', - 'rule_trigger_description_ends' => '', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', - 'rule_trigger_description_is' => '', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', 'rule_action_set_category' => 'Set category to ":action_value"', 'rule_action_clear_category' => 'Clear category', From 245f06c93a7cc1232e778abb56cb51c0b97830e0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 09:17:23 +0100 Subject: [PATCH 121/276] Fix test, expand index. --- database/seeds/TestDataSeeder.php | 2 +- resources/views/rules/index.twig | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 925f17048a..d12955607a 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -215,7 +215,7 @@ class TestDataSeeder extends Seeder $rule = new Rule; $rule->user()->associate($this->user); $rule->ruleGroup()->associate(RuleGroup::find(1)); - $rule->order = 2; + $rule->order = 3; $rule->active = 1; $rule->stop_processing = 0; $rule->title = 'Move savings'; diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 829b626fdd..bdce941bae 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -63,14 +63,25 @@
    From 15d341444365e33134536bdf8279c98e2d6e6b5f Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 09:38:48 +0100 Subject: [PATCH 122/276] Can order rules. --- app/Http/Controllers/RuleController.php | 28 ++++++++- app/Repositories/Rule/RuleRepository.php | 63 +++++++++++++++++++ .../Rule/RuleRepositoryInterface.php | 18 ++++++ 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index abe2bf9dc4..0d0caa2ca1 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -184,16 +184,38 @@ class RuleController extends Controller */ public function index() { - $ruleGroups = Auth::user()->ruleGroups()->with('rules')->get(); + $ruleGroups = Auth::user()->ruleGroups()->with(['rules' => function($query) { + $query->orderBy('order','ASC'); + + }])->get(); return view('rules.index', compact('ruleGroups')); } + /** - * @param Rule $rule + * @param RuleRepositoryInterface $repository + * @param Rule $rule + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function upRule(Rule $rule) + public function upRule(RuleRepositoryInterface $repository, Rule $rule) { + $repository->moveRuleUp($rule); + + return redirect(route('rules.index')); + + } + + /** + * @param RuleRepositoryInterface $repository + * @param Rule $rule + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function downRule(RuleRepositoryInterface $repository, Rule $rule) + { + $repository->moveRuleDown($rule); + + return redirect(route('rules.index')); } diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 3a7bfb1c2d..f29681fba6 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -12,6 +12,7 @@ namespace FireflyIII\Repositories\Rule; use Auth; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; +use Log; /** * Class RuleRepository @@ -112,4 +113,66 @@ class RuleRepository implements RuleRepositoryInterface return true; } + + /** + * @param Rule $rule + * @return bool + */ + public function moveRuleUp(Rule $rule) + { + $order = $rule->order; + + // find the rule with order-1 and give it order+1 + $other = $rule->ruleGroup->rules()->where('order', ($order - 1))->first(); + if ($other) { + $other->order = ($other->order + 1); + $other->save(); + } + + $rule->order = ($rule->order - 1); + $rule->save(); + $this->resetRulesInGroupOrder($rule->ruleGroup); + } + + /** + * @param Rule $rule + * @return bool + */ + public function moveRuleDown(Rule $rule) + { + $order = $rule->order; + + // find the rule with order+1 and give it order-1 + $other = $rule->ruleGroup->rules()->where('order', ($order + 1))->first(); + if ($other) { + $other->order = $other->order - 1; + $other->save(); + } + + + $rule->order = ($rule->order + 1); + $rule->save(); + $this->resetRulesInGroupOrder($rule->ruleGroup); + } + + /** + * @return bool + */ + public function resetRulesInGroupOrder(RuleGroup $ruleGroup) + { + $ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]); + + $set = $ruleGroup->rules() + ->orderBy('order', 'ASC') + ->orderBy('updated_at', 'DESC') + ->get(); + $count = 1; + /** @var Rule $entry */ + foreach ($set as $entry) { + $entry->order = $count; + $entry->save(); + $count++; + } + + } } \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index ea4e2e1b5f..9406ef9140 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -9,6 +9,7 @@ namespace FireflyIII\Repositories\Rule; +use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; /** @@ -52,4 +53,21 @@ interface RuleRepositoryInterface */ public function resetRuleGroupOrder(); + /** + * @return bool + */ + public function resetRulesInGroupOrder(RuleGroup $ruleGroup); + + /** + * @param Rule $rule + * @return bool + */ + public function moveRuleUp(Rule $rule); + + /** + * @param Rule $rule + * @return bool + */ + public function moveRuleDown(Rule $rule); + } \ No newline at end of file From 521623797e8e7ab97c2ff9cd30757485084353bd Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 09:49:12 +0100 Subject: [PATCH 123/276] Move rule groups. --- app/Http/routes.php | 4 ++++ resources/lang/en_US/firefly.php | 4 +++- resources/views/rules/index.twig | 10 ++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index c6d261bd91..e414bec428 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -247,6 +247,10 @@ Route::group( Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleController@editRuleGroup', 'as' => 'rules.rule-group.edit']); Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); + Route::get('/rules/groups/up/{ruleGroup}', ['uses' => 'RuleController@upRuleGroup', 'as' => 'rules.rule-group.up']); + Route::get('/rules/groups/down/{ruleGroup}', ['uses' => 'RuleController@downRuleGroup', 'as' => 'rules.rule-group.down']); + + Route::post('/rules/groups/store', ['uses' => 'RuleController@storeRuleGroup', 'as' => 'rules.rule-group.store']); Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleController@updateRuleGroup', 'as' => 'rules.rule-group.update']); Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleController@destroyRuleGroup', 'as' => 'rules.rule-group.destroy']); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index fa33d0ace7..17c03e889d 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -54,9 +54,11 @@ return [ 'updated_rule_group' => 'Successfully updated rule group ":title".', 'edit_rule_group' => 'Edit rule group ":title"', 'delete_rule_group' => 'Delete rule group ":title"', - 'deleted_rule_group' => 'Deleted rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', 'update_rule_group' => 'Update rule group', 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', // actions and triggers 'rule_trigger_user_action' => 'User action is ":trigger_value"', diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index bdce941bae..cdb6cf1b09 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -36,8 +36,14 @@
    From 97770da61966ea1f753f35d86d1aa033bba99dc2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 09:53:59 +0100 Subject: [PATCH 124/276] Re-order rule groups. --- app/Http/Controllers/RuleController.php | 30 +++++++++++++- app/Repositories/Rule/RuleRepository.php | 40 +++++++++++++++++++ .../Rule/RuleRepositoryInterface.php | 12 ++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 0d0caa2ca1..563c653154 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -184,8 +184,8 @@ class RuleController extends Controller */ public function index() { - $ruleGroups = Auth::user()->ruleGroups()->with(['rules' => function($query) { - $query->orderBy('order','ASC'); + $ruleGroups = Auth::user()->ruleGroups()->orderBy('order','ASC') ->with(['rules' => function ($query) { + $query->orderBy('order', 'ASC'); }])->get(); @@ -219,4 +219,30 @@ class RuleController extends Controller } + /** + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function upRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $repository->moveRuleGroupUp($ruleGroup); + + return redirect(route('rules.index')); + + } + + /** + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function downRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $repository->moveRuleGroupDown($ruleGroup); + + return redirect(route('rules.index')); + + } + } diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index f29681fba6..71509ee640 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -175,4 +175,44 @@ class RuleRepository implements RuleRepositoryInterface } } + + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function moveRuleGroupUp(RuleGroup $ruleGroup) + { + $order = $ruleGroup->order; + + // find the rule with order-1 and give it order+1 + $other = Auth::user()->ruleGroups()->where('order', ($order - 1))->first(); + if ($other) { + $other->order = ($other->order + 1); + $other->save(); + } + + $ruleGroup->order = ($ruleGroup->order - 1); + $ruleGroup->save(); + $this->resetRuleGroupOrder(); + } + + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function moveRuleGroupDown(RuleGroup $ruleGroup) + { + $order = $ruleGroup->order; + + // find the rule with order+1 and give it order-1 + $other = Auth::user()->ruleGroups()->where('order', ($order + 1))->first(); + if ($other) { + $other->order = ($other->order - 1); + $other->save(); + } + + $ruleGroup->order = ($ruleGroup->order + 1); + $ruleGroup->save(); + $this->resetRuleGroupOrder(); + } } \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 9406ef9140..f497e0568d 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -70,4 +70,16 @@ interface RuleRepositoryInterface */ public function moveRuleDown(Rule $rule); + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function moveRuleGroupUp(RuleGroup $ruleGroup); + + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function moveRuleGroupDown(RuleGroup $ruleGroup); + } \ No newline at end of file From b9b04135106bc32f432f9042de4428c211fee012 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 10:33:24 +0100 Subject: [PATCH 125/276] More resources for rules. --- app/Http/Controllers/RuleController.php | 12 ++++++--- app/Repositories/Rule/RuleRepository.php | 25 +++++++++++++++++-- .../Rule/RuleRepositoryInterface.php | 9 ++++++- app/Support/ExpandedForm.php | 2 +- resources/lang/en_US/firefly.php | 1 + resources/views/rules/rule-group/delete.twig | 12 +++++++++ 6 files changed, 54 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 563c653154..0bae1ae214 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -10,6 +10,7 @@ namespace FireflyIII\Http\Controllers; use Auth; +use ExpandedForm; use FireflyIII\Http\Requests; use FireflyIII\Http\Requests\RuleGroupFormRequest; use FireflyIII\Models\Rule; @@ -146,16 +147,19 @@ class RuleController extends Controller * * @return \Illuminate\View\View */ - public function deleteRuleGroup(RuleGroup $ruleGroup) + public function deleteRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) { $subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]); + $ruleGroupList = Expandedform::makeSelectList($repository->getRuleGroups(), true); + unset($ruleGroupList[$ruleGroup->id]); + // put previous url in session Session::put('rules.rule-group.delete.url', URL::previous()); Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'delete-rule-group'); - return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle')); + return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle', 'ruleGroupList')); } /** @@ -168,7 +172,9 @@ class RuleController extends Controller { $title = $ruleGroup->title; - $repository->destroyRuleGroup($ruleGroup); + $moveTo = Auth::user()->ruleGroups()->find(intval(Input::get('move_rules_before_delete'))); + + $repository->destroyRuleGroup($ruleGroup, $moveTo); Session::flash('success', trans('firefly.deleted_rule_group', ['title' => $title])); diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 71509ee640..c2f03e4b2b 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -12,6 +12,7 @@ namespace FireflyIII\Repositories\Rule; use Auth; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; +use Illuminate\Support\Collection; use Log; /** @@ -77,19 +78,31 @@ class RuleRepository implements RuleRepositoryInterface /** * @param RuleGroup $ruleGroup + * @param RuleGroup $moveTo * * @return boolean */ - public function destroyRuleGroup(RuleGroup $ruleGroup) + public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null) { /** @var Rule $rule */ foreach ($ruleGroup->rules as $rule) { - $rule->delete(); + + if (is_null($moveTo)) { + + $rule->delete(); + } else { + // move + $rule->ruleGroup()->associate($moveTo); + $rule->save(); + } } $ruleGroup->delete(); $this->resetRuleGroupOrder(); + if (!is_null($moveTo)) { + $this->resetRulesInGroupOrder($moveTo); + } return true; } @@ -215,4 +228,12 @@ class RuleRepository implements RuleRepositoryInterface $ruleGroup->save(); $this->resetRuleGroupOrder(); } + + /** + * @return Collection + */ + public function getRuleGroups() + { + return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get(); + } } \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index f497e0568d..28c1d8f148 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -11,6 +11,7 @@ namespace FireflyIII\Repositories\Rule; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; +use Illuminate\Support\Collection; /** * Interface RuleRepositoryInterface @@ -43,10 +44,11 @@ interface RuleRepositoryInterface /** * @param RuleGroup $ruleGroup + * @param RuleGroup $moveTo * * @return boolean */ - public function destroyRuleGroup(RuleGroup $ruleGroup); + public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null); /** * @return bool @@ -82,4 +84,9 @@ interface RuleRepositoryInterface */ public function moveRuleGroupDown(RuleGroup $ruleGroup); + /** + * @return Collection + */ + public function getRuleGroups(); + } \ No newline at end of file diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index d9153dc098..37e4cc6c53 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -269,7 +269,7 @@ class ExpandedForm $title = null; foreach ($fields as $field) { - if (isset($entry->$field)) { + if (isset($entry->$field) && is_null($title)) { $title = $entry->$field; } } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 17c03e889d..34c16a8eb5 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -59,6 +59,7 @@ return [ 'no_rules_in_group' => 'There are no rules in this group', 'move_rule_group_up' => 'Move rule group up', 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', // actions and triggers 'rule_trigger_user_action' => 'User action is ":trigger_value"', diff --git a/resources/views/rules/rule-group/delete.twig b/resources/views/rules/rule-group/delete.twig index 7d5e8e1c1b..17aa5a2bd5 100644 --- a/resources/views/rules/rule-group/delete.twig +++ b/resources/views/rules/rule-group/delete.twig @@ -27,6 +27,18 @@ {% endif %}

    + {% if ruleGroup.rules|length > 0 %} +

    + {{ 'save_rules_by_moving'|_ }} +

    + +

    + {{ Form.select('move_rules_before_delete', ruleGroupList, null, {class: 'form-control'}) }} +

    + {% else %} + + {% endif %} +
    ",t.document[0]).appendTo(n)):"tr"===s?t._createTrPlaceholder(t.currentItem,n):"img"===s&&n.attr("src",t.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(e,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(s.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),s.placeholder.update(t,t.placeholder)},_createTrPlaceholder:function(t,i){var s=this;t.children().each(function(){e("",s.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(t){var i,s,n,a,o,r,h,l,u,d,c=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!e.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(c&&e.contains(this.containers[i].element[0],c.element[0]))continue;c=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0);if(c)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,a=null,u=c.floating||this._isFloating(this.currentItem),o=u?"left":"top",r=u?"width":"height",d=u?"clientX":"clientY",s=this.items.length-1;s>=0;s--)e.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[o],l=!1,t[d]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(t[d]-h)&&(n=Math.abs(t[d]-h),a=this.items[s],this.direction=l?"up":"down"));if(!a&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;a?this._rearrange(t,a,null,!0):this._rearrange(t,null,this.containers[p].element,!0),this._trigger("change",t,this._uiHash()),this.containers[p]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||e("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.width():this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(t=e(n.containment)[0],i=e(n.containment).offset(),s="hidden"!==e(t).css("overflow"),this.containment=[i.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]) -},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,a=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():a?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():a?0:n.scrollLeft())*s}},_generatePosition:function(t){var i,s,n=this.options,a=t.pageX,o=t.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.leftthis.containment[2]&&(a=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1],o=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((a-this.originalPageX)/n.grid[0])*n.grid[0],a=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:a-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(e,t,i,s){i?i[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(e,t){function i(e,t,i){return function(s){i._trigger(e,s,t._uiHash(t))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&n.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||n.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(n.push(function(e){this._trigger("remove",e,this._uiHash())}),n.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)t||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!t){for(s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var i=t||this;return{helper:i.helper,placeholder:i.placeholder||e([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:t?t.element:null}}});var o="ui-effects-",r=e;e.effects={effect:{}},function(e,t){function i(e,t,i){var s=d[t.type]||{};return null==e?i||!t.def?null:t.def:(e=s.floor?~~e:parseFloat(e),isNaN(e)?t.def:s.mod?(e+s.mod)%s.mod:0>e?0:e>s.max?s.max:e)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(e,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[u[l].cache]=o[u[l].cache],n=s._rgba=o._rgba,!1):t}),n.length?("0,0,0,0"===n.join()&&e.extend(n,a.transparent),s):a[i]}function n(e,t,i){return i=(i+1)%1,1>6*i?e+6*(t-e)*i:1>2*i?t:2>3*i?e+6*(t-e)*(2/3-i):e}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],l=e.Color=function(t,i,s,n){return new e.Color.fn.parse(t,i,s,n)},u={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},d={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},c=l.support={},p=e("

    ")[0],f=e.each;p.style.cssText="background-color:rgba(1,1,1,.5)",c.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),l.fn=e.extend(l.prototype,{parse:function(n,o,r,h){if(n===t)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=e(n).css(o),o=t);var d=this,c=e.type(n),p=this._rgba=[];return o!==t&&(n=[n,o,r,h],c="array"),"string"===c?this.parse(s(n)||a._default):"array"===c?(f(u.rgba.props,function(e,t){p[t.idx]=i(n[t.idx],t)}),this):"object"===c?(n instanceof l?f(u,function(e,t){n[t.cache]&&(d[t.cache]=n[t.cache].slice())}):f(u,function(t,s){var a=s.cache;f(s.props,function(e,t){if(!d[a]&&s.to){if("alpha"===e||null==n[e])return;d[a]=s.to(d._rgba)}d[a][t.idx]=i(n[e],t,!0)}),d[a]&&0>e.inArray(null,d[a].slice(0,3))&&(d[a][3]=1,s.from&&(d._rgba=s.from(d[a])))}),this):t},is:function(e){var i=l(e),s=!0,n=this;return f(u,function(e,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(e,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:t})),s}),s},_space:function(){var e=[],t=this;return f(u,function(i,s){t[s.cache]&&e.push(i)}),e.pop()},transition:function(e,t){var s=l(e),n=s._space(),a=u[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(e,n){var a=n.idx,o=r[a],l=s[a],u=d[n.type]||{};null!==l&&(null===o?h[a]=l:(u.mod&&(l-o>u.mod/2?o+=u.mod:o-l>u.mod/2&&(o-=u.mod)),h[a]=i((l-o)*t+o,n)))}),this[n](h)},blend:function(t){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(t)._rgba;return l(e.map(i,function(e,t){return(1-s)*n[t]+s*e}))},toRgbaString:function(){var t="rgba(",i=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===i[3]&&(i.pop(),t="rgb("),t+i.join()+")"},toHslaString:function(){var t="hsla(",i=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===i[3]&&(i.pop(),t="hsl("),t+i.join()+")"},toHexString:function(t){var i=this._rgba.slice(),s=i.pop();return t&&i.push(~~(255*s)),"#"+e.map(i,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,u.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,i,s=e[0]/255,n=e[1]/255,a=e[2]/255,o=e[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,u=r+h,d=.5*u;return t=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=d?l/u:l/(2-u),[Math.round(t)%360,i,d,null==o?1:o]},u.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,i=e[1],s=e[2],a=e[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,t+1/3)),Math.round(255*n(r,o,t)),Math.round(255*n(r,o,t-1/3)),a]},f(u,function(s,n){var a=n.props,o=n.cache,h=n.to,u=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===t)return this[o].slice();var n,r=e.type(s),d="array"===r||"object"===r?s:arguments,c=this[o].slice();return f(a,function(e,t){var s=d["object"===r?e:t.idx];null==s&&(s=c[t.idx]),c[t.idx]=i(s,t)}),u?(n=l(u(c)),n[o]=c,n):l(c)},f(a,function(t,i){l.fn[t]||(l.fn[t]=function(n){var a,o=e.type(n),h="alpha"===t?this._hsla?"hsla":"rgba":s,l=this[h](),u=l[i.idx];return"undefined"===o?u:("function"===o&&(n=n.call(this,u),o=e.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=u+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(t){var i=t.split(" ");f(i,function(t,i){e.cssHooks[i]={set:function(t,n){var a,o,r="";if("transparent"!==n&&("string"!==e.type(n)||(a=s(n)))){if(n=l(a||n),!c.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?t.parentNode:t;(""===r||"transparent"===r)&&o&&o.style;)try{r=e.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{t.style[i]=n}catch(h){}}},e.fx.step[i]=function(t){t.colorInit||(t.start=l(t.elem,i),t.end=l(t.end),t.colorInit=!0),e.cssHooks[i].set(t.elem,t.start.transition(t.end,t.pos))}})},l.hook(o),e.cssHooks.borderColor={expand:function(e){var t={};return f(["Top","Right","Bottom","Left"],function(i,s){t["border"+s+"Color"]=e}),t}},a=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(r),function(){function t(t){var i,s,n=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[e.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function i(t,i){var s,a,o={};for(s in i)a=i[s],t[s]!==a&&(n[s]||(e.fx.step[s]||!isNaN(parseFloat(a)))&&(o[s]=a));return o}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,i){e.fx.step[i]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(r.style(e.elem,i,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(n,a,o,r){var h=e.speed(a,o,r);return this.queue(function(){var a,o=e(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var i=e(this);return{el:i,start:t(this)}}),a=function(){e.each(s,function(e,t){n[t]&&o[t+"Class"](n[t])})},a(),l=l.map(function(){return this.end=t(this.el[0]),this.diff=i(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var t=this,i=e.Deferred(),s=e.extend({},h,{queue:!1,complete:function(){i.resolve(t)}});return this.el.animate(this.diff,s),i.promise()}),e.when.apply(e,l.get()).done(function(){a(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),h.complete.call(o[0])})})},e.fn.extend({addClass:function(t){return function(i,s,n,a){return s?e.effects.animateClass.call(this,{add:i},s,n,a):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(i,s,n,a){return arguments.length>1?e.effects.animateClass.call(this,{remove:i},s,n,a):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(i,s,n,a,o){return"boolean"==typeof s||void 0===s?n?e.effects.animateClass.call(this,s?{add:i}:{remove:i},n,a,o):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:i},s,n,a)}}(e.fn.toggleClass),switchClass:function(t,i,s,n,a){return e.effects.animateClass.call(this,{add:i,remove:t},s,n,a)}})}(),function(){function t(t,i,s,n){return e.isPlainObject(t)&&(i=t,t=t.effect),t={effect:t},null==i&&(i={}),e.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||e.fx.speeds[i])&&(n=s,s=i,i={}),e.isFunction(s)&&(n=s,s=null),i&&e.extend(t,i),s=s||i.duration,t.duration=e.fx.off?0:"number"==typeof s?s:s in e.fx.speeds?e.fx.speeds[s]:e.fx.speeds._default,t.complete=n||i.complete,t}function i(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.4",save:function(e,t){for(var i=0;t.length>i;i++)null!==t[i]&&e.data(o+t[i],e[0].style[t[i]])},restore:function(e,t){var i,s;for(s=0;t.length>s;s++)null!==t[s]&&(i=e.data(o+t[s]),void 0===i&&(i=""),e.css(t[s],i))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var i,s;switch(e[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=e[0]/t.height}switch(e[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=e[1]/t.width}return{x:s,y:i}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var i={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},s=e("

    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:t.width(),height:t.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return t.wrap(s),(t[0]===a||e.contains(t[0],a))&&e(a).focus(),s=t.parent(),"static"===t.css("position")?(s.css({position:"relative"}),t.css({position:"relative"})):(e.extend(i,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,s){i[s]=t.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(n),s.css(i).show()},removeWrapper:function(t){var i=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===i||e.contains(t[0],i))&&e(i).focus()),t},setTransition:function(t,i,s,n){return n=n||{},e.each(i,function(e,i){var a=t.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),e.fn.extend({effect:function(){function i(t){function i(){e.isFunction(a)&&a.call(n[0]),e.isFunction(t)&&t()}var n=e(this),a=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):o.call(n[0],s,i)}var s=t.apply(this,arguments),n=s.mode,a=s.queue,o=e.effects.effect[s.effect];return e.fx.off||!o?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)}):a===!1?this.each(i):this.queue(a||"fx",i)},show:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(e.fn.show),hide:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(e.fn.hide),toggle:function(e){return function(s){if(i(s)||"boolean"==typeof s)return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(e.fn.toggle),cssUnit:function(t){var i=this.css(t),s=[];return e.each(["em","px","%","pt"],function(e,t){i.indexOf(t)>0&&(s=[parseFloat(i),t])}),s}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,i){t[i]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,i=4;((t=Math.pow(2,--i))-1)/11>e;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,i){e.easing["easeIn"+t]=i,e.easing["easeOut"+t]=function(e){return 1-i(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?i(2*e)/2:1-i(-2*e+2)/2}})}(),e.effects}); \ No newline at end of file +(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/^(input|select|textarea|button|object)$/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function s(e){for(var t,i;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(i=parseInt(e.css("zIndex"),10),!isNaN(i)&&0!==i))return i;e=e.parent()}return 0}function n(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.regional.en=e.extend(!0,{},this.regional[""]),this.regional["en-US"]=e.extend(!0,{},this.regional.en),this.dpDiv=a(e("
    "))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",o)}function o(){e.datepicker._isDisabledDatepicker(v.inline?v.dpDiv.parent()[0]:v.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))}function r(t,i){e.extend(t,i);for(var s in i)null==i[s]&&(t[s]=i[s]);return t}function h(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("
    ").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var l=0,u=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,n=u.call(arguments,1),a=0,o=n.length;o>a;a++)for(i in n[a])s=n[a][i],n[a].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(n){var a="string"==typeof n,o=u.call(arguments,1),r=this;return a?this.each(function(){var i,a=e.data(this,s);return"instance"===n?(r=a,!1):a?e.isFunction(a[n])&&"_"!==n.charAt(0)?(i=a[n].apply(a,o),i!==a&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+n+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+n+"'")}):(o.length&&(n=e.widget.extend.apply(null,[n].concat(o))),this.each(function(){var t=e.data(this,s);t?(t.option(n||{}),t._init&&t._init()):e.data(this,s,new i(n,this))})),r}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=l++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var d=!1;e(document).mouseup(function(){d=!1}),e.widget("ui.mouse",{version:"1.11.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!d){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),d=!0,!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button)return this._mouseUp(t);if(!t.which)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),d=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("
    "),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.widthi?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(M,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.accordion",{version:"1.11.4",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,s=this.headers.length,n=this.headers.index(t.target),a=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:a=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:a=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:a=this.headers[0];break;case i.END:a=this.headers[s-1]}a&&(e(t.target).attr("tabIndex",-1),e(a).attr("tabIndex",0),a.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var e=this.headers,t=this.panels;this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.panels=this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide(),t&&(this._off(e.not(this.headers)),this._off(t.not(this.panels)))},_refresh:function(){var t,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),i=t.uniqueId().attr("id"),s=t.next(),n=s.uniqueId().attr("id");t.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(t=n.height(),this.element.siblings(":visible").each(function(){var i=e(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(t-=i.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n[0]===s[0],o=a&&i.collapsible,r=o?e():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:o?e():n,newPanel:r};t.preventDefault(),a&&!i.collapsible||this._trigger("beforeActivate",t,l)===!1||(i.active=o?!1:this.headers.index(n),this.active=a?e():n,this._toggle(l),s.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),a||(n.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&n.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,s=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,t):(s.hide(),i.show(),this._toggleComplete(t)),s.attr({"aria-hidden":"true"}),s.prev().attr({"aria-selected":"false","aria-expanded":"false"}),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===parseInt(e(this).attr("tabIndex"),10)}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(e,t,i){var s,n,a,o=this,r=0,h=e.css("box-sizing"),l=e.length&&(!t.length||e.index()",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var i=e(t.target);!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){if(!this.previousFilter){var i=e(t.currentTarget); +i.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,i)}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var i=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,i)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){var i,s,n,a,o=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:o=!1,s=this.previousFilter||"",n=String.fromCharCode(t.keyCode),a=!1,clearTimeout(this.filterTimer),n===s?a=!0:n=s+n,i=this._filterMenuItems(n),i=a&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(t.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(t,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}o&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,i,s=this,n=this.options.icons.submenu,a=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),a.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),i=t.parent(),s=e("").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);i.attr("aria-haspopup","true").prepend(s),t.attr("aria-labelledby",i.attr("id"))}),t=a.add(this.element),i=t.find(this.options.items),i.not(".ui-menu-item").each(function(){var t=e(this);s._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),i.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var i,s;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),s=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=t.children(".ui-menu"),i.length&&e&&/^mouse/.test(e.type)&&this._startOpening(i),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,n=t.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=t.outerHeight(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var i=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(t,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(t),this.activeMenu=s},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,i){var s;this.active&&(s="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[t]()),this.focus(i,s)},nextPage:function(t){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=e(this),0>i.offset().top-s-n}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=e(this),i.offset().top-s+n>0}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,void 0;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("
    {{ 'rule_name'|_ }}{{ 'rule_triggers'|_ }}{{ 'rule_actions'|_ }}
    {{ 'rule_name'|_ }}{{ 'rule_triggers'|_ }}{{ 'rule_actions'|_ }}
    - - - - + + + +
    XY + {% if rule.ruleTriggers.count > 0 %} +
      + {% for trigger in rule.ruleTriggers %} + {% if trigger.trigger_type != "user_action" %} +
    • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }}
    • + {% endif %} + {% endfor %} +
    + {% endif %} +
    + {% if rule.ruleActions.count > 0 %} +
      + {% for action in rule.ruleActions %} +
    • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }}
    • + {% endfor %} +
    + {% endif %} +
    + {% if rule.order > 1 %} + class="fa fa-fw fa-arrow-up"> + {% else %} + + {% endif %} + {% if rule.order < ruleGroup.rules.count %} + class="btn btn-default"> + + {% else %} + + {% endif %} + class="fa fa-fw fa-pencil"> + class="fa fa-fw fa-trash">
     
    "+"",C=d?"":"",x=0;7>x;x++)N=(x+u)%7,C+="";for(M+=C+"",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),P=(this._getFirstDayOfMonth(et,Z)-u+7)%7,I=Math.ceil((P+A)/7),H=Q?this.maxRows>I?this.maxRows:I:I,this.maxRows=H,z=this._daylightSavingAdjust(new Date(et,Z,1-P)),F=0;H>F;F++){for(M+="",E=d?"":"",x=0;7>x;x++)O=g?g.apply(e.input?e.input[0]:null,[z]):[!0,""],j=z.getMonth()!==Z,W=j&&!y||!O[0]||X&&X>z||$&&z>$,E+="",z.setDate(z.getDate()+1),z=this._daylightSavingAdjust(z);M+=E+""}Z++,Z>11&&(Z=0,et++),M+="
    "+this._get(e,"weekHeader")+"=5?" class='ui-datepicker-week-end'":"")+">"+""+p[N]+"
    "+this._get(e,"calculateWeek")(z)+""+(j&&!v?" ":W?""+z.getDate()+"":""+z.getDate()+"")+"
    "+(Q?"
    "+(K[0]>0&&T===K[1]-1?"
    ":""):""),k+=M}_+=k}return _+=l,e._keyEvent=!1,_},_generateMonthYearHeader:function(e,t,i,s,n,a,o,r){var h,l,u,d,c,p,f,m,g=this._get(e,"changeMonth"),v=this._get(e,"changeYear"),y=this._get(e,"showMonthAfterYear"),b="
    ",_="";if(a||!g)_+=""+o[t]+"";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,_+=""}if(y||(b+=_+(!a&&g&&v?"":" ")),!e.yearshtml)if(e.yearshtml="",a||!v)b+=""+i+"";else{for(d=this._get(e,"yearRange").split(":"),c=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?c+parseInt(e,10):parseInt(e,10);return isNaN(t)?c:t},f=p(d[0]),m=Math.max(f,p(d[1]||"")),f=s?Math.max(f,s.getFullYear()):f,m=n?Math.min(m,n.getFullYear()):m,e.yearshtml+="",b+=e.yearshtml,e.yearshtml=null}return b+=this._get(e,"yearSuffix"),y&&(b+=(!a&&g&&v?"":" ")+_),b+="
    "},_adjustInstDate:function(e,t,i){var s=e.drawYear+("Y"===i?t:0),n=e.drawMonth+("M"===i?t:0),a=Math.min(e.selectedDay,this._getDaysInMonth(s,n))+("D"===i?t:0),o=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(s,n,a)));e.selectedDay=o.getDate(),e.drawMonth=e.selectedMonth=o.getMonth(),e.drawYear=e.selectedYear=o.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),s=this._getMinMaxDate(e,"max"),n=i&&i>t?i:t;return s&&n>s?s:n},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,s){var n=this._getNumberOfMonths(e),a=this._daylightSavingAdjust(new Date(i,s+(0>t?t:n[0]*n[1]),1));return 0>t&&a.setDate(this._getDaysInMonth(a.getFullYear(),a.getMonth())),this._isInRange(e,a)},_isInRange:function(e,t){var i,s,n=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),o=null,r=null,h=this._get(e,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),o=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(o+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||t.getTime()>=n.getTime())&&(!a||t.getTime()<=a.getTime())&&(!o||t.getFullYear()>=o)&&(!r||r>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,s){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var n=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(s,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),n,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new n,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.11.4",e.datepicker,e.widget("ui.draggable",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var i=this.options;return this._blurActiveElement(t),this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(t){this.iframeBlocks=this.document.find(t).map(function(){var t=e(this);return e("
    ").css("position","absolute").appendTo(t.parent()).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(t){var i=this.document[0];if(this.handleElement.is(t.target))try{i.activeElement&&"body"!==i.activeElement.nodeName.toLowerCase()&&e(i.activeElement).blur()}catch(s){}},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===e(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(t),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._normalizeRightBottom(),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_refreshOffsets:function(e){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top}},_mouseDrag:function(t,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return this._unblockFrames(),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.handleElement.is(t.target)&&this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this.handleElement.addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.handleElement.removeClass("ui-draggable-handle")},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper),n=s?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options,a=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,e(a).width()-this.helperProportions.width-this.margins.left,(e(a).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0) +},_convertPositionTo:function(e,t){t||(t=this.position);var i="absolute"===e?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:t.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(e,t){var i,s,n,a,o=this.options,r=this._isRootNode(this.scrollParent[0]),h=e.pageX,l=e.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.lefti[2]&&(h=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a),"y"===o.axis&&(h=this.originalPageX),"x"===o.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_normalizeRightBottom:function(){"y"!==this.options.axis&&"auto"!==this.helper.css("right")&&(this.helper.width(this.helper.width()),this.helper.css("right","auto")),"x"!==this.options.axis&&"auto"!==this.helper.css("bottom")&&(this.helper.height(this.helper.height()),this.helper.css("bottom","auto"))},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s,this],!0),/^(drag|start|stop)/.test(t)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i,s){var n=e.extend({},i,{item:s.element});s.sortables=[],e(s.options.connectToSortable).each(function(){var i=e(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",t,n))})},stop:function(t,i,s){var n=e.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,e.each(s.sortables,function(){var e=this;e.isOver?(e.isOver=0,s.cancelHelperRemoval=!0,e.cancelHelperRemoval=!1,e._storedCSS={position:e.placeholder.css("position"),top:e.placeholder.css("top"),left:e.placeholder.css("left")},e._mouseStop(t),e.options.helper=e.options._helper):(e.cancelHelperRemoval=!0,e._trigger("deactivate",t,n))})},drag:function(t,i,s){e.each(s.sortables,function(){var n=!1,a=this;a.positionAbs=s.positionAbs,a.helperProportions=s.helperProportions,a.offset.click=s.offset.click,a._intersectsWith(a.containerCache)&&(n=!0,e.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==a&&this._intersectsWith(this.containerCache)&&e.contains(a.element[0],this.element[0])&&(n=!1),n})),n?(a.isOver||(a.isOver=1,s._parent=i.helper.parent(),a.currentItem=i.helper.appendTo(a.element).data("ui-sortable-item",!0),a.options._helper=a.options.helper,a.options.helper=function(){return i.helper[0]},t.target=a.currentItem[0],a._mouseCapture(t,!0),a._mouseStart(t,!0,!0),a.offset.click.top=s.offset.click.top,a.offset.click.left=s.offset.click.left,a.offset.parent.left-=s.offset.parent.left-a.offset.parent.left,a.offset.parent.top-=s.offset.parent.top-a.offset.parent.top,s._trigger("toSortable",t),s.dropped=a.element,e.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,a.fromOutside=s),a.currentItem&&(a._mouseDrag(t),i.position=a.position)):a.isOver&&(a.isOver=0,a.cancelHelperRemoval=!0,a.options._revert=a.options.revert,a.options.revert=!1,a._trigger("out",t,a._uiHash(a)),a._mouseStop(t,!0),a.options.revert=a.options._revert,a.options.helper=a.options._helper,a.placeholder&&a.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(t),i.position=s._generatePosition(t,!0),s._trigger("fromSortable",t),s.dropped=!1,e.each(s.sortables,function(){this.refreshPositions()}))})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,i,s){var n=e("body"),a=s.options;n.css("cursor")&&(a._cursor=n.css("cursor")),n.css("cursor",a.cursor)},stop:function(t,i,s){var n=s.options;n._cursor&&e("body").css("cursor",n._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("opacity")&&(a._opacity=n.css("opacity")),n.css("opacity",a.opacity)},stop:function(t,i,s){var n=s.options;n._opacity&&e(i.helper).css("opacity",n._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(t,i,s){var n=s.options,a=!1,o=s.scrollParentNotHidden[0],r=s.document[0];o!==r&&"HTML"!==o.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+o.offsetHeight-t.pageY=0;c--)h=s.snapElements[c].left-s.margins.left,l=h+s.snapElements[c].width,u=s.snapElements[c].top-s.margins.top,d=u+s.snapElements[c].height,h-m>v||g>l+m||u-m>b||y>d+m||!e.contains(s.snapElements[c].item.ownerDocument,s.snapElements[c].item)?(s.snapElements[c].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(n=m>=Math.abs(u-b),a=m>=Math.abs(d-y),o=m>=Math.abs(h-v),r=m>=Math.abs(l-g),n&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left)),p=n||a||o||r,"outer"!==f.snapMode&&(n=m>=Math.abs(u-y),a=m>=Math.abs(d-b),o=m>=Math.abs(h-g),r=m>=Math.abs(l-v),n&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d-s.helperProportions.height,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[c].snapping&&(n||a||o||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=n||a||o||r||p)}}),e.ui.plugin.add("draggable","stack",{start:function(t,i,s){var n,a=s.options,o=e.makeArray(e(a.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});o.length&&(n=parseInt(e(o[0]).css("zIndex"),10)||0,e(o).each(function(t){e(this).css("zIndex",n+t)}),this.css("zIndex",n+o.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("zIndex")&&(a._zIndex=n.css("zIndex")),n.css("zIndex",a.zIndex)},stop:function(t,i,s){var n=s.options;n._zIndex&&e(i.helper).css("zIndex",n._zIndex)}}),e.ui.draggable,e.widget("ui.resizable",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(e){return parseInt(e,10)||0},_isNumber:function(e){return!isNaN(parseInt(e,10))},_hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return t[s]>0?!0:(t[s]=1,n=t[s]>0,t[s]=0,n)},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(e("
    ").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=e(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("
    "),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=e(this.handles[i]),this._on(this.handles[i],{mousedown:o._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(t){var i,s,n,a=this.options,o=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),a.containment&&(i+=e(a.containment).scrollLeft()||0,s+=e(a.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:o.width(),height:o.height()},this.originalSize=this._helper?{width:o.outerWidth(),height:o.outerHeight()}:{width:o.width(),height:o.height()},this.sizeDiff={width:o.outerWidth()-o.width(),height:o.outerHeight()-o.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof a.aspectRatio?a.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===n?this.axis+"-resize":n),o.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var i,s,n=this.originalMousePosition,a=this.axis,o=t.pageX-n.left||0,r=t.pageY-n.top||0,h=this._change[a];return this._updatePrevProperties(),h?(i=h.apply(this,[t,o,r]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var e={};return this.position.top!==this.prevPosition.top&&(e.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(e.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(e.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(e.height=this.size.height+"px"),this.helper.css(e),e},_updateVirtualBoundaries:function(e){var t,i,s,n,a,o=this.options;a={minWidth:this._isNumber(o.minWidth)?o.minWidth:0,maxWidth:this._isNumber(o.maxWidth)?o.maxWidth:1/0,minHeight:this._isNumber(o.minHeight)?o.minHeight:0,maxHeight:this._isNumber(o.maxHeight)?o.maxHeight:1/0},(this._aspectRatio||e)&&(t=a.minHeight*this.aspectRatio,s=a.minWidth/this.aspectRatio,i=a.maxHeight*this.aspectRatio,n=a.maxWidth/this.aspectRatio,t>a.minWidth&&(a.minWidth=t),s>a.minHeight&&(a.minHeight=s),a.maxWidth>i&&(a.maxWidth=i),a.maxHeight>n&&(a.maxHeight=n)),this._vBoundaries=a},_updateCache:function(e){this.offset=this.helper.offset(),this._isNumber(e.left)&&(this.position.left=e.left),this._isNumber(e.top)&&(this.position.top=e.top),this._isNumber(e.height)&&(this.size.height=e.height),this._isNumber(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,i=this.size,s=this.axis;return this._isNumber(e.height)?e.width=e.height*this.aspectRatio:this._isNumber(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===s&&(e.left=t.left+(i.width-e.width),e.top=null),"nw"===s&&(e.top=t.top+(i.height-e.height),e.left=t.left+(i.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,i=this.axis,s=this._isNumber(e.width)&&t.maxWidth&&t.maxWidthe.width,o=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,r=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,l=/sw|nw|w/.test(i),u=/nw|ne|n/.test(i);return a&&(e.width=t.minWidth),o&&(e.height=t.minHeight),s&&(e.width=t.maxWidth),n&&(e.height=t.maxHeight),a&&l&&(e.left=r-t.minWidth),s&&l&&(e.left=r-t.maxWidth),o&&u&&(e.top=h-t.minHeight),n&&u&&(e.top=h-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_getPaddingPlusBorderDimensions:function(e){for(var t=0,i=[],s=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],n=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];4>t;t++)i[t]=parseInt(s[t],10)||0,i[t]+=parseInt(n[t],10)||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var e,t=0,i=this.helper||this.element;this._proportionallyResizeElements.length>t;t++)e=this._proportionallyResizeElements[t],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(e)),e.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("
    "),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var t,i,s,n,a,o,r,h=e(this).resizable("instance"),l=h.options,u=h.element,d=l.containment,c=d instanceof e?d.get(0):/parent/.test(d)?u.parent().get(0):d;c&&(h.containerElement=e(c),/document/.test(d)||d===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(t=e(c),i=[],e(["Top","Right","Left","Bottom"]).each(function(e,s){i[e]=h._num(t.css("padding"+s))}),h.containerOffset=t.offset(),h.containerPosition=t.position(),h.containerSize={height:t.innerHeight()-i[3],width:t.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,a=h.containerSize.width,o=h._hasScroll(c,"left")?c.scrollWidth:a,r=h._hasScroll(c)?c.scrollHeight:n,h.parentData={element:c,left:s.left,top:s.top,width:o,height:r}))},resize:function(t){var i,s,n,a,o=e(this).resizable("instance"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,d={top:0,left:0},c=o.containerElement,p=!0;c[0]!==document&&/static/.test(c.css("position"))&&(d=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-d.left),u&&(o.size.height=o.size.width/o.aspectRatio,p=!1),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio,p=!1),o.position.top=o._helper?h.top:0),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a?(o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top):(o.offset.left=o.element.offset().left,o.offset.top=o.element.offset().top),i=Math.abs(o.sizeDiff.width+(o._helper?o.offset.left-d.left:o.offset.left-h.left)),s=Math.abs(o.sizeDiff.height+(o._helper?o.offset.top-d.top:o.offset.top-h.top)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio,p=!1)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio,p=!1)),p||(o.position.left=o.prevPosition.left,o.position.top=o.prevPosition.top,o.size.width=o.prevSize.width,o.size.height=o.prevSize.height)},stop:function(){var t=e(this).resizable("instance"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).resizable("instance"),i=t.options;e(i.alsoResize).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})},resize:function(t,i){var s=e(this).resizable("instance"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0};e(n.alsoResize).each(function(){var t=e(this),s=e(this).data("ui-resizable-alsoresize"),n={},a=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(a,function(e,t){var i=(s[t]||0)+(r[t]||0);i&&i>=0&&(n[t]=i||null)}),t.css(n)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).resizable("instance"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t,i=e(this).resizable("instance"),s=i.options,n=i.size,a=i.originalSize,o=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,u=h[1]||1,d=Math.round((n.width-a.width)/l)*l,c=Math.round((n.height-a.height)/u)*u,p=a.width+d,f=a.height+c,m=s.maxWidth&&p>s.maxWidth,g=s.maxHeight&&f>s.maxHeight,v=s.minWidth&&s.minWidth>p,y=s.minHeight&&s.minHeight>f;s.grid=h,v&&(p+=l),y&&(f+=u),m&&(p-=l),g&&(f-=u),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=o.top-c):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=o.left-d):((0>=f-u||0>=p-l)&&(t=i._getPaddingPlusBorderDimensions(this)),f-u>0?(i.size.height=f,i.position.top=o.top-c):(f=u-t.height,i.size.height=f,i.position.top=o.top+a.height-f),p-l>0?(i.size.width=p,i.position.left=o.left-d):(p=l-t.width,i.size.width=p,i.position.left=o.left+a.width-p))}}),e.ui.resizable,e.widget("ui.dialog",{version:"1.11.4",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"Close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i,s=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(":focusable").focus().length)try{i=this.document[0].activeElement,i&&"body"!==i.nodeName.toLowerCase()&&e(i).blur()}catch(n){}this._hide(this.uiDialog,this.options.hide,function(){s._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+e(this).css("z-index")}).get(),a=Math.max.apply(null,n);return a>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",a+1),s=!0),s&&!i&&this._trigger("focus",t),s},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var e=this._focusedElement;e||(e=this.element.find("[autofocus]")),e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("
    ").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),void 0; +if(t.keyCode===e.ui.keyCode.TAB&&!t.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");t.target!==n[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==s[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){n.focus()}),t.preventDefault()):(this._delay(function(){s.focus()}),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("
    ").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html(" "),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("
    ").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),void 0):(e.each(i,function(i,s){var n,a;s=e.isFunction(s)?{click:s,text:i}:s,s=e.extend({type:"button"},s),n=s.click,s.click=function(){n.apply(t.element[0],arguments)},a={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,e("",s).button(a).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,t(n))},drag:function(e,s){i._trigger("drag",e,t(s))},stop:function(n,a){var o=a.offset.left-i.document.scrollLeft(),r=a.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(o>=0?"+":"")+o+" "+"top"+(r>=0?"+":"")+r,of:i.window},e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,t(a))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,s=this.options,n=s.resizable,a=this.uiDialog.css("position"),o="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:o,start:function(s,n){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,t(n))},resize:function(e,s){i._trigger("resize",e,t(s))},stop:function(n,a){var o=i.uiDialog.offset(),r=o.left-i.document.scrollLeft(),h=o.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(h>=0?"+":"")+h,of:i.window},e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,t(a))}}).css("position",a)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._makeFocusTarget(),this._focusedElement=e(t.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var t=this._trackingInstances(),i=e.inArray(this,t);-1!==i&&t.splice(i,1)},_trackingInstances:function(){var e=this.document.data("ui-dialog-instances");return e||(e=[],this.document.data("ui-dialog-instances",e)),e},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(t){var i=this,s=!1,n={};e.each(t,function(e,t){i._setOption(e,t),e in i.sizeRelatedOptions&&(s=!0),e in i.resizableRelatedOptions&&(n[e]=t)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,t){var i,s,n=this.uiDialog;"dialogClass"===e&&n.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=n.is(":data(ui-draggable)"),i&&!t&&n.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(s=n.is(":data(ui-resizable)"),s&&!t&&n.resizable("destroy"),s&&"string"==typeof t&&n.resizable("option","handles",t),s||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),e=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),t=Math.max(0,s.minHeight-e),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-e):"none","auto"===s.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("
    ").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=!0;this._delay(function(){t=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(e){t||this._allowInteraction(e)||(e.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=e("
    ").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var e=this.document.data("ui-dialog-overlays")-1;e?this.document.data("ui-dialog-overlays",e):this.document.unbind("focusin").removeData("ui-dialog-overlays"),this.overlay.remove(),this.overlay=null}}}),e.widget("ui.droppable",{version:"1.11.4",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(s)?s:function(e){return e.is(s)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,i){if("accept"===t)this.accept=e.isFunction(i)?i:function(e){return e.is(i)};else if("scope"===t){var s=e.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(t,i)},_activate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",t,this.ui(i))},_deactivate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",t,this.ui(i))},_over:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(i)))},_out:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(i)))},_drop:function(t,i){var s=i||e.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=e(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&e.ui.intersect(s,e.extend(i,{offset:i.element.offset()}),i.options.tolerance,t)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(s)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,i){return e>=t&&t+i>e}return function(t,i,s,n){if(!i.offset)return!1;var a=(t.positionAbs||t.position.absolute).left+t.margins.left,o=(t.positionAbs||t.position.absolute).top+t.margins.top,r=a+t.helperProportions.width,h=o+t.helperProportions.height,l=i.offset.left,u=i.offset.top,d=l+i.proportions().width,c=u+i.proportions().height;switch(s){case"fit":return a>=l&&d>=r&&o>=u&&c>=h;case"intersect":return a+t.helperProportions.width/2>l&&d>r-t.helperProportions.width/2&&o+t.helperProportions.height/2>u&&c>h-t.helperProportions.height/2;case"pointer":return e(n.pageY,u,i.proportions().height)&&e(n.pageX,l,i.proportions().width);case"touch":return(o>=u&&c>=o||h>=u&&c>=h||u>o&&h>c)&&(a>=l&&d>=a||r>=l&&d>=r||l>a&&r>d);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,i){var s,n,a=e.ui.ddmanager.droppables[t.options.scope]||[],o=i?i.type:null,r=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||t&&!a[s].accept.call(a[s].element[0],t.currentItem||t.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue e}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(t,i){var s=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(t,i){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)})},drag:function(t,i){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,i),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=e.ui.intersect(t,this,this.options.tolerance,i),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===n}),a.length&&(s=e(a[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(t,i){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)}},e.ui.droppable;var y="ui-effects-",b=e;e.effects={effect:{}},function(e,t){function i(e,t,i){var s=d[t.type]||{};return null==e?i||!t.def?null:t.def:(e=s.floor?~~e:parseFloat(e),isNaN(e)?t.def:s.mod?(e+s.mod)%s.mod:0>e?0:e>s.max?s.max:e)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(e,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[u[l].cache]=o[u[l].cache],n=s._rgba=o._rgba,!1):t}),n.length?("0,0,0,0"===n.join()&&e.extend(n,a.transparent),s):a[i]}function n(e,t,i){return i=(i+1)%1,1>6*i?e+6*(t-e)*i:1>2*i?t:2>3*i?e+6*(t-e)*(2/3-i):e}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],l=e.Color=function(t,i,s,n){return new e.Color.fn.parse(t,i,s,n)},u={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},d={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},c=l.support={},p=e("

    ")[0],f=e.each;p.style.cssText="background-color:rgba(1,1,1,.5)",c.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),l.fn=e.extend(l.prototype,{parse:function(n,o,r,h){if(n===t)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=e(n).css(o),o=t);var d=this,c=e.type(n),p=this._rgba=[];return o!==t&&(n=[n,o,r,h],c="array"),"string"===c?this.parse(s(n)||a._default):"array"===c?(f(u.rgba.props,function(e,t){p[t.idx]=i(n[t.idx],t)}),this):"object"===c?(n instanceof l?f(u,function(e,t){n[t.cache]&&(d[t.cache]=n[t.cache].slice())}):f(u,function(t,s){var a=s.cache;f(s.props,function(e,t){if(!d[a]&&s.to){if("alpha"===e||null==n[e])return;d[a]=s.to(d._rgba)}d[a][t.idx]=i(n[e],t,!0)}),d[a]&&0>e.inArray(null,d[a].slice(0,3))&&(d[a][3]=1,s.from&&(d._rgba=s.from(d[a])))}),this):t},is:function(e){var i=l(e),s=!0,n=this;return f(u,function(e,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(e,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:t})),s}),s},_space:function(){var e=[],t=this;return f(u,function(i,s){t[s.cache]&&e.push(i)}),e.pop()},transition:function(e,t){var s=l(e),n=s._space(),a=u[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(e,n){var a=n.idx,o=r[a],l=s[a],u=d[n.type]||{};null!==l&&(null===o?h[a]=l:(u.mod&&(l-o>u.mod/2?o+=u.mod:o-l>u.mod/2&&(o-=u.mod)),h[a]=i((l-o)*t+o,n)))}),this[n](h)},blend:function(t){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(t)._rgba;return l(e.map(i,function(e,t){return(1-s)*n[t]+s*e}))},toRgbaString:function(){var t="rgba(",i=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===i[3]&&(i.pop(),t="rgb("),t+i.join()+")"},toHslaString:function(){var t="hsla(",i=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===i[3]&&(i.pop(),t="hsl("),t+i.join()+")"},toHexString:function(t){var i=this._rgba.slice(),s=i.pop();return t&&i.push(~~(255*s)),"#"+e.map(i,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,u.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,i,s=e[0]/255,n=e[1]/255,a=e[2]/255,o=e[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,u=r+h,d=.5*u;return t=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=d?l/u:l/(2-u),[Math.round(t)%360,i,d,null==o?1:o]},u.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,i=e[1],s=e[2],a=e[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,t+1/3)),Math.round(255*n(r,o,t)),Math.round(255*n(r,o,t-1/3)),a]},f(u,function(s,n){var a=n.props,o=n.cache,h=n.to,u=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===t)return this[o].slice();var n,r=e.type(s),d="array"===r||"object"===r?s:arguments,c=this[o].slice();return f(a,function(e,t){var s=d["object"===r?e:t.idx];null==s&&(s=c[t.idx]),c[t.idx]=i(s,t)}),u?(n=l(u(c)),n[o]=c,n):l(c)},f(a,function(t,i){l.fn[t]||(l.fn[t]=function(n){var a,o=e.type(n),h="alpha"===t?this._hsla?"hsla":"rgba":s,l=this[h](),u=l[i.idx];return"undefined"===o?u:("function"===o&&(n=n.call(this,u),o=e.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=u+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(t){var i=t.split(" ");f(i,function(t,i){e.cssHooks[i]={set:function(t,n){var a,o,r="";if("transparent"!==n&&("string"!==e.type(n)||(a=s(n)))){if(n=l(a||n),!c.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?t.parentNode:t;(""===r||"transparent"===r)&&o&&o.style;)try{r=e.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{t.style[i]=n}catch(h){}}},e.fx.step[i]=function(t){t.colorInit||(t.start=l(t.elem,i),t.end=l(t.end),t.colorInit=!0),e.cssHooks[i].set(t.elem,t.start.transition(t.end,t.pos))}})},l.hook(o),e.cssHooks.borderColor={expand:function(e){var t={};return f(["Top","Right","Bottom","Left"],function(i,s){t["border"+s+"Color"]=e}),t}},a=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(b),function(){function t(t){var i,s,n=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[e.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function i(t,i){var s,a,o={};for(s in i)a=i[s],t[s]!==a&&(n[s]||(e.fx.step[s]||!isNaN(parseFloat(a)))&&(o[s]=a));return o}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,i){e.fx.step[i]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(b.style(e.elem,i,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(n,a,o,r){var h=e.speed(a,o,r);return this.queue(function(){var a,o=e(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var i=e(this);return{el:i,start:t(this)}}),a=function(){e.each(s,function(e,t){n[t]&&o[t+"Class"](n[t])})},a(),l=l.map(function(){return this.end=t(this.el[0]),this.diff=i(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var t=this,i=e.Deferred(),s=e.extend({},h,{queue:!1,complete:function(){i.resolve(t)}});return this.el.animate(this.diff,s),i.promise()}),e.when.apply(e,l.get()).done(function(){a(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),h.complete.call(o[0])})})},e.fn.extend({addClass:function(t){return function(i,s,n,a){return s?e.effects.animateClass.call(this,{add:i},s,n,a):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(i,s,n,a){return arguments.length>1?e.effects.animateClass.call(this,{remove:i},s,n,a):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(i,s,n,a,o){return"boolean"==typeof s||void 0===s?n?e.effects.animateClass.call(this,s?{add:i}:{remove:i},n,a,o):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:i},s,n,a)}}(e.fn.toggleClass),switchClass:function(t,i,s,n,a){return e.effects.animateClass.call(this,{add:i,remove:t},s,n,a)}})}(),function(){function t(t,i,s,n){return e.isPlainObject(t)&&(i=t,t=t.effect),t={effect:t},null==i&&(i={}),e.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||e.fx.speeds[i])&&(n=s,s=i,i={}),e.isFunction(s)&&(n=s,s=null),i&&e.extend(t,i),s=s||i.duration,t.duration=e.fx.off?0:"number"==typeof s?s:s in e.fx.speeds?e.fx.speeds[s]:e.fx.speeds._default,t.complete=n||i.complete,t}function i(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.4",save:function(e,t){for(var i=0;t.length>i;i++)null!==t[i]&&e.data(y+t[i],e[0].style[t[i]])},restore:function(e,t){var i,s;for(s=0;t.length>s;s++)null!==t[s]&&(i=e.data(y+t[s]),void 0===i&&(i=""),e.css(t[s],i))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var i,s;switch(e[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=e[0]/t.height}switch(e[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=e[1]/t.width}return{x:s,y:i}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var i={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},s=e("

    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:t.width(),height:t.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return t.wrap(s),(t[0]===a||e.contains(t[0],a))&&e(a).focus(),s=t.parent(),"static"===t.css("position")?(s.css({position:"relative"}),t.css({position:"relative"})):(e.extend(i,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,s){i[s]=t.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(n),s.css(i).show()},removeWrapper:function(t){var i=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===i||e.contains(t[0],i))&&e(i).focus()),t},setTransition:function(t,i,s,n){return n=n||{},e.each(i,function(e,i){var a=t.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),e.fn.extend({effect:function(){function i(t){function i(){e.isFunction(a)&&a.call(n[0]),e.isFunction(t)&&t()}var n=e(this),a=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):o.call(n[0],s,i)}var s=t.apply(this,arguments),n=s.mode,a=s.queue,o=e.effects.effect[s.effect];return e.fx.off||!o?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)}):a===!1?this.each(i):this.queue(a||"fx",i)},show:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(e.fn.show),hide:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(e.fn.hide),toggle:function(e){return function(s){if(i(s)||"boolean"==typeof s)return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(e.fn.toggle),cssUnit:function(t){var i=this.css(t),s=[];return e.each(["em","px","%","pt"],function(e,t){i.indexOf(t)>0&&(s=[parseFloat(i),t])}),s}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,i){t[i]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,i=4;((t=Math.pow(2,--i))-1)/11>e;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,i){e.easing["easeIn"+t]=i,e.easing["easeOut"+t]=function(e){return 1-i(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?i(2*e)/2:1-i(-2*e+2)/2}})}(),e.effects,e.effects.effect.blind=function(t,i){var s,n,a,o=e(this),r=/up|down|vertical/,h=/up|left|vertical|horizontal/,l=["position","top","bottom","left","right","height","width"],u=e.effects.setMode(o,t.mode||"hide"),d=t.direction||"up",c=r.test(d),p=c?"height":"width",f=c?"top":"left",m=h.test(d),g={},v="show"===u;o.parent().is(".ui-effects-wrapper")?e.effects.save(o.parent(),l):e.effects.save(o,l),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n=s[p](),a=parseFloat(s.css(f))||0,g[p]=v?n:0,m||(o.css(c?"bottom":"right",0).css(c?"top":"left","auto").css({position:"absolute"}),g[f]=v?a:n+a),v&&(s.css(p,0),m||s.css(f,a+n)),s.animate(g,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){"hide"===u&&o.hide(),e.effects.restore(o,l),e.effects.removeWrapper(o),i()}})},e.effects.effect.bounce=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"effect"),l="hide"===h,u="show"===h,d=t.direction||"up",c=t.distance,p=t.times||5,f=2*p+(u||l?1:0),m=t.duration/f,g=t.easing,v="up"===d||"down"===d?"top":"left",y="up"===d||"left"===d,b=o.queue(),_=b.length;for((u||l)&&r.push("opacity"),e.effects.save(o,r),o.show(),e.effects.createWrapper(o),c||(c=o["top"===v?"outerHeight":"outerWidth"]()/3),u&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,y?2*-c:2*c).animate(a,m,g)),l&&(c/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g).animate(a,m,g),c=l?2*c:c/2;l&&(n={opacity:0},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g)),o.queue(function(){l&&o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}),_>1&&b.splice.apply(b,[1,0].concat(b.splice(_,f+1))),o.dequeue()},e.effects.effect.clip=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"hide"),l="show"===h,u=t.direction||"vertical",d="vertical"===u,c=d?"height":"width",p=d?"top":"left",f={};e.effects.save(o,r),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[c](),l&&(n.css(c,0),n.css(p,a/2)),f[c]=l?a:0,f[p]=l?0:a/2,n.animate(f,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){l||o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}})},e.effects.effect.drop=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","opacity","height","width"],o=e.effects.setMode(n,t.mode||"hide"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h?"pos":"neg",d={opacity:r?1:0};e.effects.save(n,a),n.show(),e.effects.createWrapper(n),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(l,"pos"===u?-s:s),d[l]=(r?"pos"===u?"+=":"-=":"pos"===u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.explode=function(t,i){function s(){b.push(this),b.length===d*c&&n()}function n(){p.css({visibility:"visible"}),e(b).remove(),m||p.hide(),i()}var a,o,r,h,l,u,d=t.pieces?Math.round(Math.sqrt(t.pieces)):3,c=d,p=e(this),f=e.effects.setMode(p,t.mode||"hide"),m="show"===f,g=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/c),y=Math.ceil(p.outerHeight()/d),b=[];for(a=0;d>a;a++)for(h=g.top+a*y,u=a-(d-1)/2,o=0;c>o;o++)r=g.left+o*v,l=o-(c-1)/2,p.clone().appendTo("body").wrap("
    ").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*y}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:y,left:r+(m?l*v:0),top:h+(m?u*y:0),opacity:m?0:1}).animate({left:r+(m?0:l*v),top:h+(m?0:u*y),opacity:m?1:0},t.duration||500,t.easing,s)},e.effects.effect.fade=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:t.duration,easing:t.easing,complete:i})},e.effects.effect.fold=function(t,i){var s,n,a=e(this),o=["position","top","bottom","left","right","height","width"],r=e.effects.setMode(a,t.mode||"hide"),h="show"===r,l="hide"===r,u=t.size||15,d=/([0-9]+)%/.exec(u),c=!!t.horizFirst,p=h!==c,f=p?["width","height"]:["height","width"],m=t.duration/2,g={},v={};e.effects.save(a,o),a.show(),s=e.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],d&&(u=parseInt(d[1],10)/100*n[l?0:1]),h&&s.css(c?{height:0,width:u}:{height:u,width:0}),g[f[0]]=h?n[0]:u,v[f[1]]=h?n[1]:0,s.animate(g,m,t.easing).animate(v,m,t.easing,function(){l&&a.hide(),e.effects.restore(a,o),e.effects.removeWrapper(a),i()})},e.effects.effect.highlight=function(t,i){var s=e(this),n=["backgroundImage","backgroundColor","opacity"],a=e.effects.setMode(s,t.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),e.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(o,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===a&&s.hide(),e.effects.restore(s,n),i()}})},e.effects.effect.size=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],h=["position","top","bottom","left","right","overflow","opacity"],l=["width","height","overflow"],u=["fontSize"],d=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],c=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=e.effects.setMode(o,t.mode||"effect"),f=t.restore||"effect"!==p,m=t.scale||"both",g=t.origin||["middle","center"],v=o.css("position"),y=f?r:h,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===t.mode&&"show"===p?(o.from=t.to||b,o.to=t.from||s):(o.from=t.from||("show"===p?b:s),o.to=t.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===m||"both"===m)&&(a.from.y!==a.to.y&&(y=y.concat(d),o.from=e.effects.setTransition(o,d,a.from.y,o.from),o.to=e.effects.setTransition(o,d,a.to.y,o.to)),a.from.x!==a.to.x&&(y=y.concat(c),o.from=e.effects.setTransition(o,c,a.from.x,o.from),o.to=e.effects.setTransition(o,c,a.to.x,o.to))),("content"===m||"both"===m)&&a.from.y!==a.to.y&&(y=y.concat(u).concat(l),o.from=e.effects.setTransition(o,u,a.from.y,o.from),o.to=e.effects.setTransition(o,u,a.to.y,o.to)),e.effects.save(o,y),o.show(),e.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),g&&(n=e.effects.getBaseline(g,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===m||"both"===m)&&(d=d.concat(["marginTop","marginBottom"]).concat(u),c=c.concat(["marginLeft","marginRight"]),l=r.concat(d).concat(c),o.find("*[width]").each(function(){var i=e(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()}; +f&&e.effects.save(i,l),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=e.effects.setTransition(i,d,a.from.y,i.from),i.to=e.effects.setTransition(i,d,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=e.effects.setTransition(i,c,a.from.x,i.from),i.to=e.effects.setTransition(i,c,a.to.x,i.to)),i.css(i.from),i.animate(i.to,t.duration,t.easing,function(){f&&e.effects.restore(i,l)})})),o.animate(o.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),e.effects.restore(o,y),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):e.each(["top","left"],function(e,t){o.css(t,function(t,i){var s=parseInt(i,10),n=e?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),e.effects.removeWrapper(o),i()}})},e.effects.effect.scale=function(t,i){var s=e(this),n=e.extend(!0,{},t),a=e.effects.setMode(s,t.mode||"effect"),o=parseInt(t.percent,10)||(0===parseInt(t.percent,10)?0:"hide"===a?0:100),r=t.direction||"both",h=t.origin,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},u={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=h||["middle","center"],n.restore=!0),n.from=t.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:l),n.to={height:l.height*u.y,width:l.width*u.x,outerHeight:l.outerHeight*u.y,outerWidth:l.outerWidth*u.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},e.effects.effect.puff=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"hide"),a="hide"===n,o=parseInt(t.percent,10)||150,r=o/100,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?h:{height:h.height*r,width:h.width*r,outerHeight:h.outerHeight*r,outerWidth:h.outerWidth*r}}),s.effect(t)},e.effects.effect.pulsate=function(t,i){var s,n=e(this),a=e.effects.setMode(n,t.mode||"show"),o="show"===a,r="hide"===a,h=o||"hide"===a,l=2*(t.times||5)+(h?1:0),u=t.duration/l,d=0,c=n.queue(),p=c.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),d=1),s=1;l>s;s++)n.animate({opacity:d},u,t.easing),d=1-d;n.animate({opacity:d},u,t.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&c.splice.apply(c,[1,0].concat(c.splice(p,l+1))),n.dequeue()},e.effects.effect.shake=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","height","width"],o=e.effects.setMode(n,t.mode||"effect"),r=t.direction||"left",h=t.distance||20,l=t.times||3,u=2*l+1,d=Math.round(t.duration/u),c="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},m={},g={},v=n.queue(),y=v.length;for(e.effects.save(n,a),n.show(),e.effects.createWrapper(n),f[c]=(p?"-=":"+=")+h,m[c]=(p?"+=":"-=")+2*h,g[c]=(p?"-=":"+=")+2*h,n.animate(f,d,t.easing),s=1;l>s;s++)n.animate(m,d,t.easing).animate(g,d,t.easing);n.animate(m,d,t.easing).animate(f,d/2,t.easing).queue(function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}),y>1&&v.splice.apply(v,[1,0].concat(v.splice(y,u+1))),n.dequeue()},e.effects.effect.slide=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","width","height"],o=e.effects.setMode(n,t.mode||"show"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h,d={};e.effects.save(n,a),n.show(),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(l,u?isNaN(s)?"-"+s:-s:s),d[l]=(r?u?"+=":"-=":u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.transfer=function(t,i){var s=e(this),n=e(t.to),a="fixed"===n.css("position"),o=e("body"),r=a?o.scrollTop():0,h=a?o.scrollLeft():0,l=n.offset(),u={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},d=s.offset(),c=e("
    ").appendTo(document.body).addClass(t.className).css({top:d.top-r,left:d.left-h,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(u,t.duration,t.easing,function(){c.remove(),i()})},e.widget("ui.progressbar",{version:"1.11.4",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=e("
    ").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return void 0===e?this.options.value:(this.options.value=this._constrainedValue(e),this._refreshValue(),void 0)},_constrainedValue:function(e){return void 0===e&&(e=this.options.value),this.indeterminate=e===!1,"number"!=typeof e&&(e=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,e))},_setOptions:function(e){var t=e.value;delete e.value,this._super(e),this.options.value=this._constrainedValue(t),this._refreshValue()},_setOption:function(e,t){"max"===e&&(t=Math.max(this.min,t)),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var t=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||t>this.min).toggleClass("ui-corner-right",t===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=e("
    ").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":t}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==t&&(this.oldValue=t,this._trigger("change")),t===this.options.max&&this._trigger("complete")}}),e.widget("ui.selectable",e.ui.mouse,{version:"1.11.4",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,i=this;this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(i.options.filter,i.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),i=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:i.left,top:i.top,right:i.left+t.outerWidth(),bottom:i.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("
    ")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var i=this,s=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(s.filter,this.element[0]),this._trigger("start",t),e(s.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=e.data(this,"selectable-item");s.startselected=!0,t.metaKey||t.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",t,{unselecting:s.element}))}),e(t.target).parents().addBack().each(function(){var s,n=e.data(this,"selectable-item");return n?(s=!t.metaKey&&!t.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",t,{selecting:n.element}):i._trigger("unselecting",t,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=t.pageX,h=t.pageY;return a>r&&(i=r,r=a,a=i),o>h&&(i=h,h=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:h-o}),this.selectees.each(function(){var i=e.data(this,"selectable-item"),l=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?l=!(i.left>r||a>i.right||i.top>h||o>i.bottom):"fit"===n.tolerance&&(l=i.left>a&&r>i.right&&i.top>o&&h>i.bottom),l?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",t,{selecting:i.element}))):(i.selecting&&((t.metaKey||t.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",t,{unselecting:i.element}))),i.selected&&(t.metaKey||t.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",t,{unselecting:i.element})))))}),!1}},_mouseStop:function(t){var i=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",t,{unselected:s.element})}),e(".ui-selecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",t,{selected:s.element})}),this._trigger("stop",t),this.helper.remove(),!1}}),e.widget("ui.selectmenu",{version:"1.11.4",defaultElement:"",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},i=this.element;return e.each(["min","max","step"],function(e,s){var n=i.attr(s);void 0!==n&&n.length&&(t[s]=n)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e),void 0)},mousewheel:function(e,t){if(t){if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()}},"mousedown .ui-spinner-button":function(t){function i(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(t)!==!1&&this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){return e(t.currentTarget).hasClass("ui-state-active")?this._start(t)===!1?!1:(this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*e.height())&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()},_keydown:function(t){var i=this.options,s=e.ui.keyCode;switch(t.keyCode){case s.UP:return this._repeat(null,1,t),!0;case s.DOWN:return this._repeat(null,-1,t),!0;case s.PAGE_UP:return this._repeat(null,i.page,t),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,t),!0}return!1},_uiSpinnerHtml:function(){return""},_buttonHtml:function(){return""+""+""+""+""},_start:function(e){return this.spinning||this._trigger("start",e)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(e,t,i){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,i)},e),this._spin(t*this.options.step,i)},_spin:function(e,t){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+e*this._increment(this.counter)),this.spinning&&this._trigger("spin",t,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(t){var i=this.options.incremental;return i?e.isFunction(i)?i(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return null!==this.options.min&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=""+e,i=t.indexOf(".");return-1===i?0:t.length-i-1},_adjustValue:function(e){var t,i,s=this.options;return t=null!==s.min?s.min:0,i=e-t,i=Math.round(i/s.step)*s.step,e=t+i,e=parseFloat(e.toFixed(this._precision())),null!==s.max&&e>s.max?s.max:null!==s.min&&s.min>e?s.min:e},_stop:function(e){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",e))},_setOption:function(e,t){if("culture"===e||"numberFormat"===e){var i=this._parse(this.element.val());return this.options[e]=t,this.element.val(this._format(i)),void 0}("max"===e||"min"===e||"step"===e)&&"string"==typeof t&&(t=this._parse(t)),"icons"===e&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(t.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(t.down)),this._super(e,t),"disabled"===e&&(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable"))},_setOptions:h(function(e){this._super(e)}),_parse:function(e){return"string"==typeof e&&""!==e&&(e=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(e,10,this.options.culture):+e),""===e||isNaN(e)?null:e},_format:function(e){return""===e?"":window.Globalize&&this.options.numberFormat?Globalize.format(e,this.options.numberFormat,this.options.culture):e},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var e=this.value();return null===e?!1:e===this._adjustValue(e)},_value:function(e,t){var i;""!==e&&(i=this._parse(e),null!==i&&(t||(i=this._adjustValue(i)),e=this._format(i))),this.element.val(e),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:h(function(e){this._stepUp(e)}),_stepUp:function(e){this._start()&&(this._spin((e||1)*this.options.step),this._stop())},stepDown:h(function(e){this._stepDown(e)}),_stepDown:function(e){this._start()&&(this._spin((e||1)*-this.options.step),this._stop())},pageUp:h(function(e){this._stepUp((e||1)*this.options.page)}),pageDown:h(function(e){this._stepDown((e||1)*this.options.page)}),value:function(e){return arguments.length?(h(this._value).call(this,e),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),e.widget("ui.tabs",{version:"1.11.4",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var e=/#.*$/;return function(t){var i,s;t=t.cloneNode(!1),i=t.href.replace(e,""),s=location.href.replace(e,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return t.hash.length>1&&i===s}}(),_create:function(){var t=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible),this._processTabs(),i.active=this._initialActive(),e.isArray(i.disabled)&&(i.disabled=e.unique(i.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):e(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var t=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===t&&(s&&this.tabs.each(function(i,n){return e(n).attr("aria-controls")===s?(t=i,!1):void 0}),null===t&&(t=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===t||-1===t)&&(t=this.tabs.length?0:!1)),t!==!1&&(t=this.tabs.index(this.tabs.eq(t)),-1===t&&(t=i?!1:0)),!i&&t===!1&&this.anchors.length&&(t=0),t},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var i=e(this.document[0].activeElement).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:s++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:n=!1,s--;break;case e.ui.keyCode.END:s=this.anchors.length-1;break;case e.ui.keyCode.HOME:s=0;break;case e.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case e.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}t.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),t.ctrlKey||t.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){return t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,i){function s(){return t>n&&(t=0),0>t&&(t=n),t}for(var n=this.tabs.length-1;-1!==e.inArray(s(),this.options.disabled);)t=i?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):"disabled"===e?(this._setupDisabled(t),void 0):(this._super(e,t),"collapsible"===e&&(this.element.toggleClass("ui-tabs-collapsible",t),t||this.options.active!==!1||this._activate(0)),"event"===e&&this._setupEvents(t),"heightStyle"===e&&this._setupHeightStyle(t),void 0)},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,i=this.tablist.children(":has(a[href])");t.disabled=e.map(i.filter(".ui-state-disabled"),function(e){return i.index(e)}),this._processTabs(),t.active!==!1&&this.anchors.length?this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=e()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this,i=this.tabs,s=this.anchors,n=this.panels; +this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist").delegate("> li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(i,s){var n,a,o,r=e(s).uniqueId().attr("id"),h=e(s).closest("li"),l=h.attr("aria-controls");t._isLocal(s)?(n=s.hash,o=n.substring(1),a=t.element.find(t._sanitizeSelector(n))):(o=h.attr("aria-controls")||e({}).uniqueId()[0].id,n="#"+o,a=t.element.find(n),a.length||(a=t._createPanel(o),a.insertAfter(t.panels[i-1]||t.tablist)),a.attr("aria-live","polite")),a.length&&(t.panels=t.panels.add(a)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":o,"aria-labelledby":r}),a.attr("aria-labelledby",r)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("
    ").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var i,s=0;i=this.tabs[s];s++)t===!0||-1!==e.inArray(s,t)?e(i).addClass("ui-state-disabled").attr("aria-disabled","true"):e(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var i={};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(e){e.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,s=this.element.parent();"fill"===t?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=e(this),s=t.css("position");"absolute"!==s&&"fixed"!==s&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,e(this).height("").height())}).height(i))},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?e():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):e(),u={oldTab:s,oldPanel:l,newTab:r?e():a,newPanel:h};t.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",t,u)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?e():a,this.xhr&&this.xhr.abort(),l.length||h.length||e.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),t),this._toggle(t,u))},_toggle:function(t,i){function s(){a.running=!1,a._trigger("activate",t,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var i,s=this._findActive(t);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tablist.unbind(this.eventNamespace),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),i=t.data("ui-tabs-aria-controls");i?t.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(t){var i=this.options.disabled;i!==!1&&(void 0===t?i=!1:(t=this._getIndex(t),i=e.isArray(i)?e.map(i,function(e){return e!==t?e:null}):e.map(this.tabs,function(e,i){return i!==t?i:null})),this._setupDisabled(i))},disable:function(t){var i=this.options.disabled;if(i!==!0){if(void 0===t)i=!0;else{if(t=this._getIndex(t),-1!==e.inArray(t,i))return;i=e.isArray(i)?e.merge([t],i).sort():[t]}this._setupDisabled(i)}},load:function(t,i){t=this._getIndex(t);var s=this,n=this.tabs.eq(t),a=n.find(".ui-tabs-anchor"),o=this._getPanelForTab(n),r={tab:n,panel:o},h=function(e,t){"abort"===t&&s.panels.stop(!1,!0),n.removeClass("ui-tabs-loading"),o.removeAttr("aria-busy"),e===s.xhr&&delete s.xhr};this._isLocal(a[0])||(this.xhr=e.ajax(this._ajaxSettings(a,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(n.addClass("ui-tabs-loading"),o.attr("aria-busy","true"),this.xhr.done(function(e,t,n){setTimeout(function(){o.html(e),s._trigger("load",i,r),h(n,t)},1)}).fail(function(e,t){setTimeout(function(){h(e,t)},1)})))},_ajaxSettings:function(t,i,s){var n=this;return{url:t.attr("href"),beforeSend:function(t,a){return n._trigger("beforeLoad",i,e.extend({jqXHR:t,ajaxSettings:a},s))}}},_getPanelForTab:function(t){var i=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),e.widget("ui.tooltip",{version:"1.11.4",options:{content:function(){var t=e(this).attr("title")||"";return e("").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_addDescribedBy:function(t,i){var s=(t.attr("aria-describedby")||"").split(/\s+/);s.push(i),t.data("ui-tooltip-id",i).attr("aria-describedby",e.trim(s.join(" ")))},_removeDescribedBy:function(t){var i=t.data("ui-tooltip-id"),s=(t.attr("aria-describedby")||"").split(/\s+/),n=e.inArray(i,s);-1!==n&&s.splice(n,1),t.removeData("ui-tooltip-id"),s=e.trim(s.join(" ")),s?t.attr("aria-describedby",s):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable(),this.liveRegion=e("
    ").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body)},_setOption:function(t,i){var s=this;return"disabled"===t?(this[i?"_disable":"_enable"](),this.options[t]=i,void 0):(this._super(t,i),"content"===t&&e.each(this.tooltips,function(e,t){s._updateContent(t.element)}),void 0)},_disable:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur");n.target=n.currentTarget=s.element[0],t.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var i=this,s=e(t?t.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&s.parents().each(function(){var t,s=e(this);s.data("ui-tooltip-open")&&(t=e.Event("blur"),t.target=t.currentTarget=this,i.close(t,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(t,s),this._updateContent(s,t))},_updateContent:function(e,t){var i,s=this.options.content,n=this,a=t?t.type:null;return"string"==typeof s?this._open(t,e,s):(i=s.call(e[0],function(i){n._delay(function(){e.data("ui-tooltip-open")&&(t&&(t.type=a),this._open(t,e,i))})}),i&&this._open(t,e,i),void 0)},_open:function(t,i,s){function n(e){l.of=e,o.is(":hidden")||o.position(l)}var a,o,r,h,l=e.extend({},this.options.position);if(s){if(a=this._find(i))return a.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(t&&"mouseover"===t.type?i.attr("title",""):i.removeAttr("title")),a=this._tooltip(i),o=a.tooltip,this._addDescribedBy(i,o.attr("id")),o.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),s.clone?(h=s.clone(),h.removeAttr("id").find("[id]").removeAttr("id")):h=s,e("
    ").html(h).appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:n}),n(t)):o.position(e.extend({of:i},this.options.position)),o.hide(),this._show(o,this.options.show),this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){o.is(":visible")&&(n(l.of),clearInterval(r))},e.fx.interval)),this._trigger("open",t,{tooltip:o})}},_registerCloseHandlers:function(t,i){var s={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var s=e.Event(t);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),t&&"mouseover"!==t.type||(s.mouseleave="close"),t&&"focusin"!==t.type||(s.focusout="close"),this._on(!0,i,s)},close:function(t){var i,s=this,n=e(t?t.currentTarget:this.element),a=this._find(n);return a?(i=a.tooltip,a.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),a.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(e(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&e.each(this.parents,function(t,i){e(i.element).attr("title",i.title),delete s.parents[t]}),a.closing=!0,this._trigger("close",t,{tooltip:i}),a.hiding||(a.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(t){var i=e("
    ").attr("role","tooltip").addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||"")),s=i.uniqueId().attr("id");return e("
    ").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),this.tooltips[s]={element:t,tooltip:i}},_find:function(e){var t=e.data("ui-tooltip-id");return t?this.tooltips[t]:null},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur"),a=s.element;n.target=n.currentTarget=a[0],t.close(n,!0),e("#"+i).remove(),a.data("ui-tooltip-title")&&(a.attr("title")||a.attr("title",a.data("ui-tooltip-title")),a.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}})}); \ No newline at end of file diff --git a/public/js/rules/index.js b/public/js/rules/index.js new file mode 100644 index 0000000000..e6c7514257 --- /dev/null +++ b/public/js/rules/index.js @@ -0,0 +1,64 @@ +/* global comboChart,token, billID */ + +// Return a helper with preserved width of cells +var fixHelper = function (e, tr) { + var $originals = tr.children(); + var $helper = tr.clone(); + $helper.children().each(function (index) { + // Set helper cell sizes to match the original sizes + $(this).width($originals.eq(index).width()); + }); + return $helper; +}; + +$(function () { + "use strict"; + console.log("Hello"); + $('.rule-triggers').sortable({ + helper: fixHelper, + stop: sortStop, + cursor: "move" + + } + ); + + $('.rule-actions').sortable({ + helper: fixHelper, + stop: sortStop, + cursor: "move" + + } + ); + } +); + + +function sortStop(event, ui) { + "use strict"; + var current = $(ui.item); + var parent = current.parent(); + var ruleId = current.parent().data('id'); + var entries = []; + // who am i? + console.log('Rule: #' + current.parent().data('id')); + + $.each(parent.children(), function (i, v) { + var trigger = $(v); + var id = trigger.data('id'); + var order = i + 1; + entries.push(id); + //console.log('Entry #' + id + ' will get order ' + order + ' in rule ' + ruleId + '.'); + + }); + if (parent.hasClass('rule-triggers')) { + $.post('rules/rules/trigger/reorder/' + ruleId, {_token: token, triggers: entries}).fail(function () { + alert('Could not re-order rule triggers. Please refresh the page.') + }); + } else { + $.post('rules/rules/action/reorder/' + ruleId, {_token: token, actions: entries}).fail(function () { + alert('Could not re-order rule actions. Please refresh the page.') + }); + + } + +} \ No newline at end of file diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index cdb6cf1b09..777597a6e8 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -34,16 +34,22 @@
    - +
    @@ -70,23 +76,30 @@
    {% if rule.order > 1 %} - - {% else %} - - {% endif %} - {% if rule.order < ruleGroup.rules.count %} - - + {% else %} {% endif %} - + + {% else %} + + {% endif %} + -
    @@ -98,10 +111,10 @@ {% if rule.ruleTriggers.count > 0 %} -
      +
        {% for trigger in rule.ruleTriggers %} {% if trigger.trigger_type != "user_action" %} -
      • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }}
      • +
      • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }}
      • {% endif %} {% endfor %}
      @@ -109,9 +122,9 @@ {% if rule.ruleActions.count > 0 %} -
        +
          {% for action in rule.ruleActions %} -
        • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }}
        • +
        • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }}
        • {% endfor %}
        {% endif %} @@ -142,3 +155,7 @@
    {% endblock %} +{% block scripts %} + + +{% endblock %} \ No newline at end of file From c352eb0c7408afcc2cf811b75b1fc58bd7e70353 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 12:37:49 +0100 Subject: [PATCH 127/276] First attempt at create rule form. --- app/Http/Controllers/RuleController.php | 22 ++++++- app/Http/routes.php | 7 +- resources/lang/en_US/firefly.php | 2 + resources/lang/en_US/form.php | 1 + resources/views/rules/index.twig | 20 +++++- resources/views/rules/rule/create.twig | 85 +++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 resources/views/rules/rule/create.twig diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 97bab1aef6..2ccd3425dd 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -52,13 +52,33 @@ class RuleController extends Controller if (Session::get('rules.rule-group.create.fromStore') !== true) { Session::put('rules.rule-group.create.url', URL::previous()); } - Session::forget('accounts.create.fromStore'); + Session::forget('rules.rule-group.create.fromStore'); Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'create-rule-group'); return view('rules.rule-group.create', compact('subTitleIcon', 'what', 'subTitle')); } + /** + * @param RuleGroup $ruleGroup + * @return View + */ + public function createRule(RuleGroup $ruleGroup) + { + $subTitleIcon = 'fa-clone'; + $subTitle = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]); + + // put previous url in session if not redirect from store (not "create another"). + if (Session::get('rules.rule.create.fromStore') !== true) { + Session::put('rules.rule.create.url', URL::previous()); + } + Session::forget('rules.rule.create.fromStore'); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'create-rule-group'); + + return view('rules.rule.create', compact('subTitleIcon','ruleGroup', 'subTitle')); + } + /** * @param RuleGroupFormRequest $request * @param RuleRepositoryInterface $repository diff --git a/app/Http/routes.php b/app/Http/routes.php index 75027595eb..c4b54e6cf5 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -236,7 +236,8 @@ Route::group( Route::get('/rules', ['uses' => 'RuleController@index', 'as' => 'rules.index']); // rules: - Route::get('/rules/rules/create', ['uses' => 'RuleController@createRule', 'as' => 'rules.rule.create']); + Route::get('/rules/create/{ruleGroup}', ['uses' => 'RuleController@createRule', 'as' => 'rules.rule.create']); + Route::get('/rules/rules/up/{rule}', ['uses' => 'RuleController@upRule', 'as' => 'rules.rule.up']); Route::get('/rules/rules/down/{rule}', ['uses' => 'RuleController@downRule', 'as' => 'rules.rule.down']); Route::get('/rules/rules/edit/{rule}', ['uses' => 'RuleController@editRule', 'as' => 'rules.rule.edit']); @@ -245,6 +246,10 @@ Route::group( Route::post('/rules/rules/trigger/reorder/{rule}', ['uses' => 'RuleController@reorderRuleTriggers']); Route::post('/rules/rules/action/reorder/{rule}', ['uses' => 'RuleController@reorderRuleActions']); + Route::post('/rules/store/{ruleGroup}', ['uses' => 'RuleController@storeRule', 'as' => 'rules.rule.store']); + Route::post('/rules/update/{rule}', ['uses' => 'RuleController@updateRule', 'as' => 'rules.rule.update']); + Route::post('/rules/destroy/{rule}', ['uses' => 'RuleController@destroyRule', 'as' => 'rules.rule.destroy']); + // rule groups: Route::get('/rules/groups/create', ['uses' => 'RuleController@createRuleGroup', 'as' => 'rules.rule-group.create']); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 34c16a8eb5..20ee206ba0 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -60,6 +60,8 @@ return [ 'move_rule_group_up' => 'Move rule group up', 'move_rule_group_down' => 'Move rule group down', 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', + 'make_new_rule' => 'Make new rule in rule group ":title"', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', // actions and triggers 'rule_trigger_user_action' => 'User action is ":trigger_value"', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 8b3b34f4c6..3ff3349a6b 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -67,6 +67,7 @@ return [ 'filename' => 'File name', 'mime' => 'Mime type', 'size' => 'Size', + 'stop_processing' => 'Stop processing', 'delete_account' => 'Delete account ":name"', diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 777597a6e8..238a4a542b 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -105,16 +105,26 @@ {{ rule.title }} + {% if rule.stop_processing %} + + {% endif %} + {% if rule.description != "" %}
    {{ rule.description }}
    {% endif %} + {% if rule.ruleTriggers.count > 0 %}
      {% for trigger in rule.ruleTriggers %} {% if trigger.trigger_type != "user_action" %} -
    • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }}
    • +
    • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }} + + {% if trigger.stop_processing %} + + {% endif %} +
    • {% endif %} {% endfor %}
    @@ -124,7 +134,11 @@ {% if rule.ruleActions.count > 0 %}
      {% for action in rule.ruleActions %} -
    • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }}
    • +
    • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }} + {% if action.stop_processing %} + + {% endif %} +
    • {% endfor %}
    {% endif %} @@ -140,7 +154,7 @@ {% endif %}


    - {{ 'new_rule'|_ }} + {{ 'new_rule'|_ }}

    diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig new file mode 100644 index 0000000000..64534e0b71 --- /dev/null +++ b/resources/views/rules/rule/create.twig @@ -0,0 +1,85 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + {{ Form.open({'class' : 'form-horizontal','id' : 'store','url' : route('rules.rule.store', ruleGroup.id)}) }} + +
    +
    +
    +
    +

    {{ 'mandatoryFields'|_ }}

    +
    +
    + {{ ExpandedForm.text('title') }} + {{ ExpandedForm.checkbox('stop_processing',1,null, {helpText: trans('firefly.rule_help_stop_processing')}) }} +
    +
    +
    +
    + + +
    +
    +

    {{ 'optionalFields'|_ }}

    +
    +
    + {{ ExpandedForm.textarea('description') }} +
    +
    +
    +
    + + +
    +
    +
    +
    +

    {{ 'rule_triggers'|_ }}

    +
    +
    + Here +
    +
    +
    +
    + + +
    +
    +
    +
    +

    {{ 'rule_actions'|_ }}

    +
    +
    + Here +
    +
    +
    +
    + +
    +
    + +
    +
    +

    {{ 'options'|_ }}

    +
    +
    + {{ ExpandedForm.optionsList('create','rule-group') }} +
    + +
    + +
    + +
    + {{ Form.close|raw }} + + +{% endblock %} From e02657a7c779dc1b20d92647667cc69e7c38ab84 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 13:12:20 +0100 Subject: [PATCH 128/276] New font awesome --- public/font-awesome/css/font-awesome.css | 2086 +++++++++++++++++ public/font-awesome/css/font-awesome.min.css | 4 +- public/font-awesome/fonts/FontAwesome.otf | Bin 93888 -> 109688 bytes .../fonts/fontawesome-webfont.eot | Bin 60767 -> 70807 bytes .../fonts/fontawesome-webfont.svg | 134 +- .../fonts/fontawesome-webfont.ttf | Bin 122092 -> 142072 bytes .../fonts/fontawesome-webfont.woff | Bin 71508 -> 83588 bytes .../fonts/fontawesome-webfont.woff2 | Bin 56780 -> 66624 bytes 8 files changed, 2200 insertions(+), 24 deletions(-) create mode 100644 public/font-awesome/css/font-awesome.css diff --git a/public/font-awesome/css/font-awesome.css b/public/font-awesome/css/font-awesome.css new file mode 100644 index 0000000000..b2a5fe2f25 --- /dev/null +++ b/public/font-awesome/css/font-awesome.css @@ -0,0 +1,2086 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.5.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} diff --git a/public/font-awesome/css/font-awesome.min.css b/public/font-awesome/css/font-awesome.min.css index 24fcc04c4e..d0603cb4b0 100644 --- a/public/font-awesome/css/font-awesome.min.css +++ b/public/font-awesome/css/font-awesome.min.css @@ -1,4 +1,4 @@ /*! - * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.3.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.3.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"} \ No newline at end of file + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} diff --git a/public/font-awesome/fonts/FontAwesome.otf b/public/font-awesome/fonts/FontAwesome.otf index f7936cc1e789eea5438d576d6b12de20191da09d..3ed7f8b48ad9bfab52eb03822fefcd6b77d2e680 100644 GIT binary patch delta 49805 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 bVD`Me8{@L|h6Tb^+P4gNsQL&zXv_Zqyz?a` delta 34003 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 Hc9H)FrD*d- diff --git a/public/font-awesome/fonts/fontawesome-webfont.eot b/public/font-awesome/fonts/fontawesome-webfont.eot index 33b2bb80055cc480e797de704925acaba4ba7d7d..9b6afaedc0fd7aaf927a07f82da9c11022251b8b 100644 GIT binary patch literal 70807 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 literal 60767 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%0DKehr}RFC`2XTB_?==0w^)u1m5PYii@6f)6_5Ydu zv+NIZ_(Rt}Q++LT5!n8!J4x!>sE&v_3*cXat{Zq5;17w;B6$epw}$Rg`0nFJg5D-L zYvw@(goc5TeJjM($AJAZxZHZN}RzBcP0=_>ZI6WVGU zO#Nk-YqZTa3{!84P0K~GsI#32<+_AsXU43wILwZS(8n%S9)lP!Dg$$e2$$9$E?^Nj zql4do#+a8qEP(bD2)DpP|$dp<`TZ#bY6^~7Xv_Lle)77^OsVhMOm(@ z??8O8kA%}ZWpR&2v!7qFSw@TF6d*=9YT^Rtk(n8p=CQWvt1Om=n&5uP;GiT6 zMRvbm39kbp*KB`qoVg12w52Z)T}`X41P>D|q_%K#zuhwb+BpEogY0E)KnSy#@+(m5 z20@LG@LUEvk`I|OIUV^^0_YtG9AElBS!Dsh%k^P9r0moJ25Lkm-gh#igwBDhAOj0!EF&8MxV^-m1U1MEd?H7} zL;r;tfFIT|ei3-Z@gyM=!%Ba7Pa626JRAA`V<2D<{RLRT@0o=bE)XF)nFtUL67`2L z{?_Qz_`Yy2t+I)?9&z#z__Q%L3pnhN}U z_rN#WU)kD59D4whbSYERHY01jM7id50EuI1ctl?<_IT=Y5vP>(sNNkB&U5&F&^kBhm5y{o!y!F+4wdxXoy;!4$W`?_nL(+bK_QDAMUV1O0AwZ| z6j)s}9YEZbY-C^Y)9Ej`aS&~{sXCG2SS3ce$EY;Yv-c8TlrD$C85ATlLZpGP_YWfi z`RQ?z1@zIfa{yqfsUDMEPpwuX%XHdO+ASb3EPi1fBPocvfgsC0xa^CG2SWBPWQ&GS zpCXPti8b>WkYbf#Vg%A?&_UwUsUQE_t4GX?7QqUpKJ2Iw#%)Q4Ft(`9Ja&Yk{C@38 z@%T`)#wWy(kKfEH;ZBQ(m*Iq&L=<)4D7tNO{SsA4Fp4D?(Ex6nQS&f3TK|atgj`fE z2|OX0(&(ZqxJd~IANX&dvX?U14_<~h2(lP6k^H8ep;2HW6oPo?U%v{M>|{sU~;p zLTv$OTx3H^4zNUn4wUfo>j{CEvTC@C+cw+cW*ABH6u@!M2EdBL?1GbL_#e;7YDBas zic?MTazk(khXSyPeDom_I~wkLv?Wr8<%egEfM!*M9^kl$>zsVzaP}S!gcD3;Czy#58RTm?`p)RTS8I<-sC3+*n{A)P*rU!@Npj`e{x9xsif2v zTW`{q3p^?A!Mk60Q{(FLt(&TVe9z z0-!PiOV02JcNeq?AbJaI+B9xC;LB=}Ho0vH(@;Qe0zq~-8ckOa!(u@Wou`p_TR|QT z38H`lJE$G{q1egUX@&v$x7wNLWD#j*!D58GLv^bT+jpdKBrK#SsQsWK(+RO40VA^w z0nA7MN1Y1Fc#5JkwD5TtHG1t;lo=i)U+kFG?1Jh11h9382!marrRE2eZh;JGh`wNO zQA_~n?%97HOKLA^#oG(5*bgSllS%rOc(S%Yj00cYR;!D9G_90{pfq7D4I*$k?byOV zR|epi%oIJ{ou`5zS!-_dnxOa{uNv)(luMo^5TCOItq}2}sxCztLEzBGS)Mf6dzaw< z!GweAgvFYJu&mH(Vl9HJBV%=Jz~~i%nDGIF9ncTET-AQ=fv{L11&K_;ei!iht(!De;ym|y7ksL|^5Ko~B-vSh80++s?unD}bZaYa@ zPH4M$&fw;xEGN3_H1vHW><%-+dg7dfW)F8$bB+h7sThoOtteO(v{&-+iK}r$%G))# z*Nhx^!ZMj1VeG?EkWg+0CYQSX1t96fV9^3c+9C393LU&CHsFCa1q99$`zTMsEWwLc zxsw1|A?k8-m8HCrk6;K7dhNDJN3R9iws%6vTq_}PtR2CZ8TG;ltZ4I}sU+^s8`P3F5QxrypG1-{ zGlr^7$Wsy(lo=xfC~BpKfg<2z4OEeEF@~x{Pi7O#CvqMJy+f+}=CB_$&IuEslB@s# J000000038FvZ??8 diff --git a/public/font-awesome/fonts/fontawesome-webfont.svg b/public/font-awesome/fonts/fontawesome-webfont.svg index 1ee89d4368..d05688e9e2 100644 --- a/public/font-awesome/fonts/fontawesome-webfont.svg +++ b/public/font-awesome/fonts/fontawesome-webfont.svg @@ -1,6 +1,6 @@ - + @@ -219,8 +219,8 @@ - - + + @@ -362,7 +362,7 @@ - + @@ -399,7 +399,7 @@ - + @@ -410,9 +410,9 @@ - - - + + + @@ -454,12 +454,12 @@ - + - + @@ -483,13 +483,13 @@ - + - + @@ -523,7 +523,7 @@ - + @@ -531,18 +531,18 @@ - + - + - + - + @@ -555,11 +555,101 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/font-awesome/fonts/fontawesome-webfont.ttf b/public/font-awesome/fonts/fontawesome-webfont.ttf index ed9372f8ea0fbaa04f42630a48887e4b38945345..26dea7951a73079223b50653c455c5adf46a4648 100644 GIT binary patch delta 25866 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 delta 6689 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 diff --git a/public/font-awesome/fonts/fontawesome-webfont.woff b/public/font-awesome/fonts/fontawesome-webfont.woff index 8b280b98fa2fa261aa4b0f8fd061f772073ef83e..dc35ce3c2cf688c89b0bd0d4a82bc4be82b14c40 100644 GIT binary patch delta 83467 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≠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 F0{}OVJyiez delta 71328 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 x!4xu>LIzXFU62nVnrKX+wfW(HgQ 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 From 5b1d9e1a0d620cb34346703da04114946e195aa9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 16:41:15 +0100 Subject: [PATCH 129/276] Start work on adding a rule. --- app/Http/Controllers/JsonController.php | 47 + app/Http/Controllers/RuleController.php | 27 +- app/Http/routes.php | 3 + public/js/rules/create-edit.js | 41 + public/js/rules/edit.js | 27 + public/js/rules/index.js | 8 +- resources/lang/en_US/firefly.php | 958 ++++++++++---------- resources/lang/en_US/form.php | 1 + resources/views/rules/partials/action.twig | 22 + resources/views/rules/partials/trigger.twig | 22 + resources/views/rules/rule/create.twig | 41 +- 11 files changed, 733 insertions(+), 464 deletions(-) create mode 100644 public/js/rules/create-edit.js create mode 100644 public/js/rules/edit.js create mode 100644 resources/views/rules/partials/action.twig create mode 100644 resources/views/rules/partials/trigger.twig diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index c94f886cf6..6122b42f6f 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -2,13 +2,17 @@ use Amount; use Carbon\Carbon; +use Config; use FireflyIII\Helpers\Report\ReportQueryInterface; +use FireflyIII\Models\RuleAction; +use FireflyIII\Models\RuleTrigger; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; +use Input; use Preferences; use Response; use Session; @@ -38,6 +42,49 @@ class JsonController extends Controller return Response::json('true'); } + /** + * @param RuleTrigger|null $trigger + * + * @return \Illuminate\Http\JsonResponse + */ + public function trigger(RuleTrigger $trigger = null) + { + $count = intval(Input::get('count')) > 0 ? intval(Input::get('count')) : 1; + $keys = array_keys(Config::get('firefly.rule-triggers')); + $triggers = []; + foreach ($keys as $key) { + if ($key != 'user_action') { + $triggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice'); + } + } + + $view = view('rules.partials.trigger', compact('triggers', 'trigger', 'count'))->render(); + + + return Response::json(['html' => $view]); + } + + + /** + * @param RuleAction|null $action + * + * @return \Illuminate\Http\JsonResponse + */ + public function action(RuleAction $action = null) + { + $count = intval(Input::get('count')) > 0 ? intval(Input::get('count')) : 1; + $keys = array_keys(Config::get('firefly.rule-actions')); + $actions = []; + foreach ($keys as $key) { + $actions[$key] = trans('firefly.rule_action_' . $key . '_choice'); + } + + $view = view('rules.partials.action', compact('actions', 'action', 'count'))->render(); + + + return Response::json(['html' => $view]); + } + /** * */ diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 2ccd3425dd..0070aa6ed7 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -61,6 +61,18 @@ class RuleController extends Controller /** * @param RuleGroup $ruleGroup + * + * @return View + */ + public function storeRule(RuleGroup $ruleGroup) + { + echo '
    ';
    +        var_dump(Input::all());exit();
    +    }
    +
    +    /**
    +     * @param RuleGroup $ruleGroup
    +     *
          * @return View
          */
         public function createRule(RuleGroup $ruleGroup)
    @@ -68,6 +80,13 @@ class RuleController extends Controller
             $subTitleIcon = 'fa-clone';
             $subTitle     = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]);
     
    +        // mandatory field: rule triggers on update-journal or store-journal.
    +        $journalTriggers = [
    +            'store-journal'  => trans('firefly.rule_trigger_store_journal'),
    +            'update-journal' => trans('firefly.rule_trigger_update_journal')
    +        ];
    +
    +
             // put previous url in session if not redirect from store (not "create another").
             if (Session::get('rules.rule.create.fromStore') !== true) {
                 Session::put('rules.rule.create.url', URL::previous());
    @@ -76,7 +95,7 @@ class RuleController extends Controller
             Session::flash('gaEventCategory', 'rules');
             Session::flash('gaEventAction', 'create-rule-group');
     
    -        return view('rules.rule.create', compact('subTitleIcon','ruleGroup', 'subTitle'));
    +        return view('rules.rule.create', compact('subTitleIcon', 'ruleGroup', 'subTitle', 'journalTriggers'));
         }
     
         /**
    @@ -208,6 +227,7 @@ class RuleController extends Controller
         /**
          * @param RuleRepositoryInterface $repository
          * @param Rule                    $rule
    +     *
          * @return \Illuminate\Http\JsonResponse
          */
         public function reorderRuleTriggers(RuleRepositoryInterface $repository, Rule $rule)
    @@ -224,6 +244,7 @@ class RuleController extends Controller
         /**
          * @param RuleRepositoryInterface $repository
          * @param Rule                    $rule
    +     *
          * @return \Illuminate\Http\JsonResponse
          */
         public function reorderRuleActions(RuleRepositoryInterface $repository, Rule $rule)
    @@ -268,6 +289,7 @@ class RuleController extends Controller
         /**
          * @param RuleRepositoryInterface $repository
          * @param Rule                    $rule
    +     *
          * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
          */
         public function upRule(RuleRepositoryInterface $repository, Rule $rule)
    @@ -281,6 +303,7 @@ class RuleController extends Controller
         /**
          * @param RuleRepositoryInterface $repository
          * @param Rule                    $rule
    +     *
          * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
          */
         public function downRule(RuleRepositoryInterface $repository, Rule $rule)
    @@ -294,6 +317,7 @@ class RuleController extends Controller
         /**
          * @param RuleRepositoryInterface $repository
          * @param RuleGroup               $ruleGroup
    +     *
          * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
          */
         public function upRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup)
    @@ -307,6 +331,7 @@ class RuleController extends Controller
         /**
          * @param RuleRepositoryInterface $repository
          * @param RuleGroup               $ruleGroup
    +     *
          * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
          */
         public function downRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup)
    diff --git a/app/Http/routes.php b/app/Http/routes.php
    index c4b54e6cf5..5c9041ebbc 100644
    --- a/app/Http/routes.php
    +++ b/app/Http/routes.php
    @@ -186,6 +186,9 @@ Route::group(
         Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']);
         Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals');
     
    +    Route::get('/json/trigger/{RuleTrigger?}', ['uses' => 'JsonController@trigger', 'as' => 'json.trigger']);
    +    Route::get('/json/action/{RuleAction?}', ['uses' => 'JsonController@action', 'as' => 'json.action']);
    +
         /**
          * New user Controller
          */
    diff --git a/public/js/rules/create-edit.js b/public/js/rules/create-edit.js
    new file mode 100644
    index 0000000000..6ba0fa2c2f
    --- /dev/null
    +++ b/public/js/rules/create-edit.js
    @@ -0,0 +1,41 @@
    +/*
    + * create-edit.js
    + * Copyright (C) 2016 Sander Dorigo
    + *
    + * This software may be modified and distributed under the terms
    + * of the MIT license.  See the LICENSE file for details.
    + */
    +
    +var triggerCount = 0;
    +var actionCount = 0;
    +
    +$(function () {
    +    "use strict";
    +    console.log("create-edit");
    +
    +});
    +
    +
    +function addNewTrigger() {
    +    "use strict";
    +    triggerCount++;
    +
    +    $.getJSON('json/trigger', {count: triggerCount}).success(function (data) {
    +        //console.log(data.html);
    +        $('tbody.rule-trigger-tbody').append(data.html);
    +    }).fail(function () {
    +        alert('Cannot get a new trigger.');
    +    });
    +}
    +
    +function addNewAction() {
    +    "use strict";
    +    triggerCount++;
    +
    +    $.getJSON('json/action', {count: actionCount}).success(function (data) {
    +        //console.log(data.html);
    +        $('tbody.rule-action-tbody').append(data.html);
    +    }).fail(function () {
    +        alert('Cannot get a new action.');
    +    });
    +}
    \ No newline at end of file
    diff --git a/public/js/rules/edit.js b/public/js/rules/edit.js
    new file mode 100644
    index 0000000000..581ece4714
    --- /dev/null
    +++ b/public/js/rules/edit.js
    @@ -0,0 +1,27 @@
    +/*
    + * edit.js
    + * Copyright (C) 2016 Sander Dorigo
    + *
    + * This software may be modified and distributed under the terms
    + * of the MIT license.  See the LICENSE file for details.
    + */
    +
    +// make a line.
    +
    +$(function () {
    +    "use strict";
    +    console.log("edit");
    +    addNewTrigger();
    +    addNewAction();
    +    $('.add_rule_trigger').click(function () {
    +        addNewTrigger();
    +
    +        return false;
    +    });
    +
    +    $('.add_rule_action').click(function () {
    +        addNewAction();
    +
    +        return false;
    +    });
    +});
    diff --git a/public/js/rules/index.js b/public/js/rules/index.js
    index e6c7514257..9c73dd7d17 100644
    --- a/public/js/rules/index.js
    +++ b/public/js/rules/index.js
    @@ -1,4 +1,11 @@
     /* global comboChart,token, billID */
    +/*
    + * index.js
    + * Copyright (C) 2016 Sander Dorigo
    + *
    + * This software may be modified and distributed under the terms
    + * of the MIT license.  See the LICENSE file for details.
    + */
     
     // Return a helper with preserved width of cells
     var fixHelper = function (e, tr) {
    @@ -13,7 +20,6 @@ var fixHelper = function (e, tr) {
     
     $(function () {
             "use strict";
    -        console.log("Hello");
             $('.rule-triggers').sortable({
                     helper: fixHelper,
                     stop: sortStop,
    diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
    index 20ee206ba0..b48de632fb 100644
    --- a/resources/lang/en_US/firefly.php
    +++ b/resources/lang/en_US/firefly.php
    @@ -2,66 +2,75 @@
     
     return [
         // general stuff:
    -    'language_incomplete'                => 'This language is not yet fully translated',
    -    'test'                               => 'You have selected English.',
    -    'close'                              => 'Close',
    -    'pleaseHold'                         => 'Please hold...',
    -    'actions'                            => 'Actions',
    -    'edit'                               => 'Edit',
    -    'delete'                             => 'Delete',
    -    'welcomeBack'                        => 'What\'s playing?',
    -    'everything'                         => 'Everything',
    -    'customRange'                        => 'Custom range',
    -    'apply'                              => 'Apply',
    -    'cancel'                             => 'Cancel',
    -    'from'                               => 'From',
    -    'to'                                 => 'To',
    -    'total_sum'                          => 'Total sum',
    -    'period_sum'                         => 'Sum for period',
    -    'showEverything'                     => 'Show everything',
    -    'never'                              => 'Never',
    -    'search_results_for'                 => 'Search results for ":query"',
    -    'bounced_error'                      => 'The message sent to :email bounced, so no access for you.',
    -    'deleted_error'                      => 'These credentials do not match our records.',
    -    'general_blocked_error'              => 'Your account has been disabled, so you cannot login.',
    -    'removed_amount'                     => 'Removed :amount',
    -    'added_amount'                       => 'Added :amount',
    -    'asset_account_role_help'            => 'Any extra options resulting from your choice can be set later.',
    -    'Opening balance'                    => 'Opening balance',
    -    'create_new_stuff'                   => 'Create new stuff',
    -    'new_withdrawal'                     => 'New withdrawal',
    -    'new_deposit'                        => 'New deposit',
    -    'new_transfer'                       => 'New transfer',
    -    'new_asset_account'                  => 'New asset account',
    -    'new_expense_account'                => 'New expense account',
    -    'new_revenue_account'                => 'New revenue account',
    -    'new_budget'                         => 'New budget',
    -    'new_bill'                           => 'New bill',
    +    'language_incomplete'       => 'This language is not yet fully translated',
    +    'test'                      => 'You have selected English.',
    +    'close'                     => 'Close',
    +    'pleaseHold'                => 'Please hold...',
    +    'actions'                   => 'Actions',
    +    'edit'                      => 'Edit',
    +    'delete'                    => 'Delete',
    +    'welcomeBack'               => 'What\'s playing?',
    +    'everything'                => 'Everything',
    +    'customRange'               => 'Custom range',
    +    'apply'                     => 'Apply',
    +    'cancel'                    => 'Cancel',
    +    'from'                      => 'From',
    +    'to'                        => 'To',
    +    'total_sum'                 => 'Total sum',
    +    'period_sum'                => 'Sum for period',
    +    'showEverything'            => 'Show everything',
    +    'never'                     => 'Never',
    +    'search_results_for'        => 'Search results for ":query"',
    +    'bounced_error'             => 'The message sent to :email bounced, so no access for you.',
    +    'deleted_error'             => 'These credentials do not match our records.',
    +    'general_blocked_error'     => 'Your account has been disabled, so you cannot login.',
    +    'removed_amount'            => 'Removed :amount',
    +    'added_amount'              => 'Added :amount',
    +    'asset_account_role_help'   => 'Any extra options resulting from your choice can be set later.',
    +    'Opening balance'           => 'Opening balance',
    +    'create_new_stuff'          => 'Create new stuff',
    +    'new_withdrawal'            => 'New withdrawal',
    +    'new_deposit'               => 'New deposit',
    +    'new_transfer'              => 'New transfer',
    +    'new_asset_account'         => 'New asset account',
    +    'new_expense_account'       => 'New expense account',
    +    'new_revenue_account'       => 'New revenue account',
    +    'new_budget'                => 'New budget',
    +    'new_bill'                  => 'New bill',
     
         // rules
    -    'rules'                              => 'Rules',
    -    'rules_explanation'                  => 'Here is going to be text',
    -    'rule_name'                          => 'Name of rule',
    -    'rule_triggers'                      => 'Rule triggers when',
    -    'rule_actions'                       => 'Rule will',
    -    'new_rule'                           => 'New rule',
    -    'new_rule_group'                     => 'New rule group',
    -    'rule_priority_up'                   => 'Give rule more priority',
    -    'rule_priority_down'                 => 'Give rule less priority',
    -    'make_new_rule_group'                => 'Make new rule group',
    -    'store_new_rule_group'               => 'Store new rule group',
    -    'created_new_rule_group'             => 'New rule group ":title" stored!',
    -    'updated_rule_group'                 => 'Successfully updated rule group ":title".',
    -    'edit_rule_group'                    => 'Edit rule group ":title"',
    -    'delete_rule_group'                  => 'Delete rule group ":title"',
    -    'deleted_rule_group'                 => 'Deleted rule group ":title"',
    -    'update_rule_group'                  => 'Update rule group',
    -    'no_rules_in_group'                  => 'There are no rules in this group',
    -    'move_rule_group_up'                 => 'Move rule group up',
    -    'move_rule_group_down'               => 'Move rule group down',
    -    'save_rules_by_moving'               => 'Save these rule(s) by moving them to another rule group:',
    -    'make_new_rule'                      => 'Make new rule in rule group ":title"',
    -    'rule_help_stop_processing'          => 'When you check this box, later rules in this group will not be executed.',
    +    'rules'                     => 'Rules',
    +    'rules_explanation'         => 'Here is going to be text',
    +    'rule_name'                 => 'Name of rule',
    +    'rule_triggers'             => 'Rule triggers when',
    +    'rule_actions'              => 'Rule will',
    +    'new_rule'                  => 'New rule',
    +    'new_rule_group'            => 'New rule group',
    +    'rule_priority_up'          => 'Give rule more priority',
    +    'rule_priority_down'        => 'Give rule less priority',
    +    'make_new_rule_group'       => 'Make new rule group',
    +    'store_new_rule_group'      => 'Store new rule group',
    +    'created_new_rule_group'    => 'New rule group ":title" stored!',
    +    'updated_rule_group'        => 'Successfully updated rule group ":title".',
    +    'edit_rule_group'           => 'Edit rule group ":title"',
    +    'delete_rule_group'         => 'Delete rule group ":title"',
    +    'deleted_rule_group'        => 'Deleted rule group ":title"',
    +    'update_rule_group'         => 'Update rule group',
    +    'no_rules_in_group'         => 'There are no rules in this group',
    +    'move_rule_group_up'        => 'Move rule group up',
    +    'move_rule_group_down'      => 'Move rule group down',
    +    'save_rules_by_moving'      => 'Save these rule(s) by moving them to another rule group:',
    +    'make_new_rule'             => 'Make new rule in rule group ":title"',
    +    'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.',
    +
    +    'trigger'                            => 'Trigger',
    +    'trigger_value'                      => 'Trigger on value',
    +    'stop_processing_other_triggers'     => 'Stop processing other triggers',
    +    'add_rule_trigger'                   => 'Add new trigger',
    +    'action'                             => 'Action',
    +    'action_value'                       => 'Action value',
    +    'stop_executing_other_actions'       => 'Stop executing other actions',
    +    'add_rule_action'                    => 'Add new action',
     
         // actions and triggers
         'rule_trigger_user_action'           => 'User action is ":trigger_value"',
    @@ -82,453 +91,484 @@ return [
         'rule_trigger_description_contains'  => 'Description contains ":trigger_value"',
         'rule_trigger_description_is'        => 'Description is ":trigger_value"',
     
    -    'rule_action_set_category'         => 'Set category to ":action_value"',
    -    'rule_action_clear_category'       => 'Clear category',
    -    'rule_action_set_budget'           => 'Set budget to ":action_value"',
    -    'rule_action_clear_budget'         => 'Clear budget',
    -    'rule_action_add_tag'              => 'Add tag ":action_value"',
    -    'rule_action_remove_tag'           => 'Remove tag ":action_value"',
    -    'rule_action_remove_all_tags'      => 'Remove all tags',
    -    'rule_action_set_description'      => 'Set description to ":action_value"',
    -    'rule_action_append_description'   => 'Append description with ":action_value"',
    -    'rule_action_prepend_description'  => 'Prepend description with ":action_value"',
    +    'rule_trigger_from_account_starts_choice'   => 'Source account starts with..',
    +    'rule_trigger_from_account_ends_choice'     => 'Source account ends with..',
    +    'rule_trigger_from_account_is_choice'       => 'Source account is..',
    +    'rule_trigger_from_account_contains_choice' => 'Source account contains..',
    +    'rule_trigger_to_account_starts_choice'     => 'Destination account starts with..',
    +    'rule_trigger_to_account_ends_choice'       => 'Destination account ends with..',
    +    'rule_trigger_to_account_is_choice'         => 'Destination account is..',
    +    'rule_trigger_to_account_contains_choice'   => 'Destination account contains..',
    +    'rule_trigger_transaction_type_choice'      => 'Transaction is of type..',
    +    'rule_trigger_amount_less_choice'           => 'Amount is less than..',
    +    'rule_trigger_amount_exactly_choice'        => 'Amount is..',
    +    'rule_trigger_amount_more_choice'           => 'Amount is more than..',
    +    'rule_trigger_description_starts_choice'    => 'Description starts with..',
    +    'rule_trigger_description_ends_choice'      => 'Description ends with..',
    +    'rule_trigger_description_contains_choice'  => 'Description contains..',
    +    'rule_trigger_description_is_choice'        => 'Description is..',
    +
    +    'rule_trigger_store_journal'  => 'When a journal is created',
    +    'rule_trigger_update_journal' => 'When a journal is updated',
    +
    +    'rule_action_set_category'        => 'Set category to ":action_value"',
    +    'rule_action_clear_category'      => 'Clear category',
    +    'rule_action_set_budget'          => 'Set budget to ":action_value"',
    +    'rule_action_clear_budget'        => 'Clear budget',
    +    'rule_action_add_tag'             => 'Add tag ":action_value"',
    +    'rule_action_remove_tag'          => 'Remove tag ":action_value"',
    +    'rule_action_remove_all_tags'     => 'Remove all tags',
    +    'rule_action_set_description'     => 'Set description to ":action_value"',
    +    'rule_action_append_description'  => 'Append description with ":action_value"',
    +    'rule_action_prepend_description' => 'Prepend description with ":action_value"',
    +
    +    'rule_action_set_category_choice'        => 'Set category to..',
    +    'rule_action_clear_category_choice'      => 'Clear any category',
    +    'rule_action_set_budget_choice'          => 'Set budget to..',
    +    'rule_action_clear_budget_choice'        => 'Clear any budget',
    +    'rule_action_add_tag_choice'             => 'Add tag..',
    +    'rule_action_remove_tag_choice'          => 'Remove tag..',
    +    'rule_action_remove_all_tags_choice'     => 'Remove all tags',
    +    'rule_action_set_description_choice'     => 'Set description to..',
    +    'rule_action_append_description_choice'  => 'Append description with..',
    +    'rule_action_prepend_description_choice' => 'Prepend description with..',
     
         // tags
    -    'store_new_tag'                    => 'Store new tag',
    -    'update_tag'                       => 'Update tag',
    -    'no_location_set'                  => 'No location set.',
    -    'meta_data'                        => 'Meta data',
    -    'location'                         => 'Location',
    +    'store_new_tag'                          => 'Store new tag',
    +    'update_tag'                             => 'Update tag',
    +    'no_location_set'                        => 'No location set.',
    +    'meta_data'                              => 'Meta data',
    +    'location'                               => 'Location',
     
         // preferences
    -    'pref_home_screen_accounts'        => 'Home screen accounts',
    -    'pref_home_screen_accounts_help'   => 'Which accounts should be displayed on the home page?',
    -    'pref_budget_settings'             => 'Budget settings',
    -    'pref_budget_settings_help'        => 'What\'s the maximum amount of money a budget envelope may contain?',
    -    'pref_view_range'                  => 'View range',
    -    'pref_view_range_help'             => 'Some charts are automatically grouped in periods. What period would you prefer?',
    -    'pref_1D'                          => 'One day',
    -    'pref_1W'                          => 'One week',
    -    'pref_1M'                          => 'One month',
    -    'pref_3M'                          => 'Three months (quarter)',
    -    'pref_6M'                          => 'Six months',
    -    'pref_languages'                   => 'Languages',
    -    'pref_languages_help'              => 'Firefly III supports several languages. Which one do you prefer?',
    -    'pref_save_settings'               => 'Save settings',
    +    'pref_home_screen_accounts'              => 'Home screen accounts',
    +    'pref_home_screen_accounts_help'         => 'Which accounts should be displayed on the home page?',
    +    'pref_budget_settings'                   => 'Budget settings',
    +    'pref_budget_settings_help'              => 'What\'s the maximum amount of money a budget envelope may contain?',
    +    'pref_view_range'                        => 'View range',
    +    'pref_view_range_help'                   => 'Some charts are automatically grouped in periods. What period would you prefer?',
    +    'pref_1D'                                => 'One day',
    +    'pref_1W'                                => 'One week',
    +    'pref_1M'                                => 'One month',
    +    'pref_3M'                                => 'Three months (quarter)',
    +    'pref_6M'                                => 'Six months',
    +    'pref_languages'                         => 'Languages',
    +    'pref_languages_help'                    => 'Firefly III supports several languages. Which one do you prefer?',
    +    'pref_save_settings'                     => 'Save settings',
     
         // profile:
    -    'change_your_password'             => 'Change your password',
    -    'delete_account'                   => 'Delete account',
    -    'current_password'                 => 'Current password',
    -    'new_password'                     => 'New password',
    -    'new_password_again'               => 'New password (again)',
    -    'delete_your_account'              => 'Delete your account',
    -    'delete_your_account_help'         => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.',
    -    'delete_your_account_password'     => 'Enter your password to continue.',
    -    'password'                         => 'Password',
    -    'are_you_sure'                     => 'Are you sure? You cannot undo this.',
    -    'delete_account_button'            => 'DELETE your account',
    -    'invalid_current_password'         => 'Invalid current password!',
    -    'password_changed'                 => 'Password changed!',
    -    'should_change'                    => 'The idea is to change your password.',
    -    'invalid_password'                 => 'Invalid password!',
    +    'change_your_password'                   => 'Change your password',
    +    'delete_account'                         => 'Delete account',
    +    'current_password'                       => 'Current password',
    +    'new_password'                           => 'New password',
    +    'new_password_again'                     => 'New password (again)',
    +    'delete_your_account'                    => 'Delete your account',
    +    'delete_your_account_help'               => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.',
    +    'delete_your_account_password'           => 'Enter your password to continue.',
    +    'password'                               => 'Password',
    +    'are_you_sure'                           => 'Are you sure? You cannot undo this.',
    +    'delete_account_button'                  => 'DELETE your account',
    +    'invalid_current_password'               => 'Invalid current password!',
    +    'password_changed'                       => 'Password changed!',
    +    'should_change'                          => 'The idea is to change your password.',
    +    'invalid_password'                       => 'Invalid password!',
     
     
         // attachments
    -    'nr_of_attachments'                => 'One attachment|:count attachments',
    -    'attachments'                      => 'Attachments',
    -    'edit_attachment'                  => 'Edit attachment ":name"',
    -    'update_attachment'                => 'Update attachment',
    -    'delete_attachment'                => 'Delete attachment ":name"',
    -    'attachment_deleted'               => 'Deleted attachment ":name"',
    -    'upload_max_file_size'             => 'Maximum file size: :size',
    +    'nr_of_attachments'                      => 'One attachment|:count attachments',
    +    'attachments'                            => 'Attachments',
    +    'edit_attachment'                        => 'Edit attachment ":name"',
    +    'update_attachment'                      => 'Update attachment',
    +    'delete_attachment'                      => 'Delete attachment ":name"',
    +    'attachment_deleted'                     => 'Deleted attachment ":name"',
    +    'upload_max_file_size'                   => 'Maximum file size: :size',
     
         // tour:
    -    'prev'                             => 'Prev',
    -    'next'                             => 'Next',
    -    'end-tour'                         => 'End tour',
    -    'pause'                            => 'Pause',
    +    'prev'                                   => 'Prev',
    +    'next'                                   => 'Next',
    +    'end-tour'                               => 'End tour',
    +    'pause'                                  => 'Pause',
     
         // transaction index
    -    'title_expenses'                   => 'Expenses',
    -    'title_withdrawal'                 => 'Expenses',
    -    'title_revenue'                    => 'Revenue / income',
    -    'title_deposit'                    => 'Revenue / income',
    -    'title_transfer'                   => 'Transfers',
    -    'title_transfers'                  => 'Transfers',
    +    'title_expenses'                         => 'Expenses',
    +    'title_withdrawal'                       => 'Expenses',
    +    'title_revenue'                          => 'Revenue / income',
    +    'title_deposit'                          => 'Revenue / income',
    +    'title_transfer'                         => 'Transfers',
    +    'title_transfers'                        => 'Transfers',
     
         // csv import:
    -    'csv_import'                       => 'Import CSV file',
    -    'csv'                              => 'CSV',
    -    'csv_index_title'                  => 'Upload and import a CSV file',
    -    'csv_define_column_roles'          => 'Define column roles',
    -    'csv_map_values'                   => 'Map found values to existing values',
    -    'csv_download_config'              => 'Download CSV configuration file.',
    -    'csv_index_text'                   => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the  button at the top of this page.',
    -    'csv_index_beta_warning'           => 'This tool is very much in beta. Please proceed with caution',
    -    'csv_header_help'                  => 'Check this box when your CSV file\'s first row consists of column names, not actual data',
    -    'csv_date_help'                    => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.',
    -    'csv_csv_file_help'                => 'Select the CSV file here. You can only upload one file at a time',
    -    'csv_csv_config_file_help'         => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.',
    -    'csv_upload_button'                => 'Start importing CSV',
    -    'csv_column_roles_title'           => 'Define column roles',
    -    'csv_column_roles_text'            => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
    -    'csv_column_roles_table'           => 'Column roles',
    -    'csv_column'                       => 'CSV column',
    -    'csv_column_name'                  => 'CSV column name',
    -    'csv_column_example'               => 'Column example data',
    -    'csv_column_role'                  => 'Column contains?',
    -    'csv_do_map_value'                 => 'Map value?',
    -    'csv_continue'                     => 'Continue to the next step',
    -    'csv_go_back'                      => 'Go back to the previous step',
    -    'csv_map_title'                    => 'Map found values to existing values',
    -    'csv_map_text'                     => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.',
    -    'csv_field_value'                  => 'Field value from CSV',
    -    'csv_field_mapped_to'              => 'Must be mapped to...',
    -    'csv_do_not_map'                   => 'Do not map this value',
    -    'csv_download_config_title'        => 'Download CSV configuration',
    -    'csv_download_config_text'         => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.',
    -    'csv_more_information_text'        => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
    -    'csv_do_download_config'           => 'Download configuration file.',
    -    'csv_empty_description'            => '(empty description)',
    -    'csv_upload_form'                  => 'CSV upload form',
    -    'csv_index_unsupported_warning'    => 'The CSV importer is yet incapable of doing the following:',
    -    'csv_unsupported_map'              => 'The importer cannot map the column ":columnRole" to existing values in the database.',
    -    'csv_unsupported_value'            => 'The importer does not know how to handle values in columns marked as ":columnRole".',
    -    'csv_cannot_store_value'           => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.',
    -    'csv_process_title'                => 'CSV import finished!',
    -    'csv_process_text'                 => 'The CSV importer has finished and has processed :rows rows',
    -    'csv_row'                          => 'Row',
    -    'csv_import_with_errors'           => 'There was one error.|There were :errors errors.',
    -    'csv_error_see_logs'               => 'Check the log files to see details.',
    -    'csv_process_new_entries'          => 'Firefly has created :imported new transaction(s).',
    -    'csv_start_over'                   => 'Import again',
    -    'csv_to_index'                     => 'Back home',
    -    'csv_upload_not_writeable'         => 'Cannot write to the path mentioned here. Cannot upload',
    -    'csv_column__ignore'               => '(ignore this column)',
    -    'csv_column_account-iban'          => 'Asset account (IBAN)',
    -    'csv_column_account-id'            => 'Asset account  ID (matching Firefly)',
    -    'csv_column_account-name'          => 'Asset account (name)',
    -    'csv_column_amount'                => 'Amount',
    -    'csv_column_bill-id'               => 'Bill ID (matching Firefly)',
    -    'csv_column_bill-name'             => 'Bill name',
    -    'csv_column_budget-id'             => 'Budget ID (matching Firefly)',
    -    'csv_column_budget-name'           => 'Budget name',
    -    'csv_column_category-id'           => 'Category ID (matching Firefly)',
    -    'csv_column_category-name'         => 'Category name',
    -    'csv_column_currency-code'         => 'Currency code (ISO 4217)',
    -    'csv_column_currency-id'           => 'Currency ID (matching Firefly)',
    -    'csv_column_currency-name'         => 'Currency name (matching Firefly)',
    -    'csv_column_currency-symbol'       => 'Currency symbol (matching Firefly)',
    -    'csv_column_date-rent'             => 'Rent calculation date',
    -    'csv_column_date-transaction'      => 'Date',
    -    'csv_column_description'           => 'Description',
    -    'csv_column_opposing-iban'         => 'Opposing account (IBAN)',
    -    'csv_column_opposing-id'           => 'Opposing account ID (matching Firefly)',
    -    'csv_column_opposing-name'         => 'Opposing account (name)',
    -    'csv_column_rabo-debet-credit'     => 'Rabobank specific debet/credit indicator',
    -    'csv_column_sepa-ct-id'            => 'SEPA Credit Transfer end-to-end ID',
    -    'csv_column_sepa-ct-op'            => 'SEPA Credit Transfer opposing account',
    -    'csv_column_sepa-db'               => 'SEPA Direct Debet',
    -    'csv_column_tags-comma'            => 'Tags (comma separated)',
    -    'csv_column_tags-space'            => 'Tags (space separated)',
    -    'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.',
    -    'csv_specifix_Dummy'               => 'Checking this has no effect whatsoever.',
    -    'csv_import_account_help'          => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.',
    -    'csv_date_parse_error'             => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?',
    +    'csv_import'                             => 'Import CSV file',
    +    'csv'                                    => 'CSV',
    +    'csv_index_title'                        => 'Upload and import a CSV file',
    +    'csv_define_column_roles'                => 'Define column roles',
    +    'csv_map_values'                         => 'Map found values to existing values',
    +    'csv_download_config'                    => 'Download CSV configuration file.',
    +    'csv_index_text'                         => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the  button at the top of this page.',
    +    'csv_index_beta_warning'                 => 'This tool is very much in beta. Please proceed with caution',
    +    'csv_header_help'                        => 'Check this box when your CSV file\'s first row consists of column names, not actual data',
    +    'csv_date_help'                          => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.',
    +    'csv_csv_file_help'                      => 'Select the CSV file here. You can only upload one file at a time',
    +    'csv_csv_config_file_help'               => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.',
    +    'csv_upload_button'                      => 'Start importing CSV',
    +    'csv_column_roles_title'                 => 'Define column roles',
    +    'csv_column_roles_text'                  => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
    +    'csv_column_roles_table'                 => 'Column roles',
    +    'csv_column'                             => 'CSV column',
    +    'csv_column_name'                        => 'CSV column name',
    +    'csv_column_example'                     => 'Column example data',
    +    'csv_column_role'                        => 'Column contains?',
    +    'csv_do_map_value'                       => 'Map value?',
    +    'csv_continue'                           => 'Continue to the next step',
    +    'csv_go_back'                            => 'Go back to the previous step',
    +    'csv_map_title'                          => 'Map found values to existing values',
    +    'csv_map_text'                           => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.',
    +    'csv_field_value'                        => 'Field value from CSV',
    +    'csv_field_mapped_to'                    => 'Must be mapped to...',
    +    'csv_do_not_map'                         => 'Do not map this value',
    +    'csv_download_config_title'              => 'Download CSV configuration',
    +    'csv_download_config_text'               => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.',
    +    'csv_more_information_text'              => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
    +    'csv_do_download_config'                 => 'Download configuration file.',
    +    'csv_empty_description'                  => '(empty description)',
    +    'csv_upload_form'                        => 'CSV upload form',
    +    'csv_index_unsupported_warning'          => 'The CSV importer is yet incapable of doing the following:',
    +    'csv_unsupported_map'                    => 'The importer cannot map the column ":columnRole" to existing values in the database.',
    +    'csv_unsupported_value'                  => 'The importer does not know how to handle values in columns marked as ":columnRole".',
    +    'csv_cannot_store_value'                 => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.',
    +    'csv_process_title'                      => 'CSV import finished!',
    +    'csv_process_text'                       => 'The CSV importer has finished and has processed :rows rows',
    +    'csv_row'                                => 'Row',
    +    'csv_import_with_errors'                 => 'There was one error.|There were :errors errors.',
    +    'csv_error_see_logs'                     => 'Check the log files to see details.',
    +    'csv_process_new_entries'                => 'Firefly has created :imported new transaction(s).',
    +    'csv_start_over'                         => 'Import again',
    +    'csv_to_index'                           => 'Back home',
    +    'csv_upload_not_writeable'               => 'Cannot write to the path mentioned here. Cannot upload',
    +    'csv_column__ignore'                     => '(ignore this column)',
    +    'csv_column_account-iban'                => 'Asset account (IBAN)',
    +    'csv_column_account-id'                  => 'Asset account  ID (matching Firefly)',
    +    'csv_column_account-name'                => 'Asset account (name)',
    +    'csv_column_amount'                      => 'Amount',
    +    'csv_column_bill-id'                     => 'Bill ID (matching Firefly)',
    +    'csv_column_bill-name'                   => 'Bill name',
    +    'csv_column_budget-id'                   => 'Budget ID (matching Firefly)',
    +    'csv_column_budget-name'                 => 'Budget name',
    +    'csv_column_category-id'                 => 'Category ID (matching Firefly)',
    +    'csv_column_category-name'               => 'Category name',
    +    'csv_column_currency-code'               => 'Currency code (ISO 4217)',
    +    'csv_column_currency-id'                 => 'Currency ID (matching Firefly)',
    +    'csv_column_currency-name'               => 'Currency name (matching Firefly)',
    +    'csv_column_currency-symbol'             => 'Currency symbol (matching Firefly)',
    +    'csv_column_date-rent'                   => 'Rent calculation date',
    +    'csv_column_date-transaction'            => 'Date',
    +    'csv_column_description'                 => 'Description',
    +    'csv_column_opposing-iban'               => 'Opposing account (IBAN)',
    +    'csv_column_opposing-id'                 => 'Opposing account ID (matching Firefly)',
    +    'csv_column_opposing-name'               => 'Opposing account (name)',
    +    'csv_column_rabo-debet-credit'           => 'Rabobank specific debet/credit indicator',
    +    'csv_column_sepa-ct-id'                  => 'SEPA Credit Transfer end-to-end ID',
    +    'csv_column_sepa-ct-op'                  => 'SEPA Credit Transfer opposing account',
    +    'csv_column_sepa-db'                     => 'SEPA Direct Debet',
    +    'csv_column_tags-comma'                  => 'Tags (comma separated)',
    +    'csv_column_tags-space'                  => 'Tags (space separated)',
    +    'csv_specifix_RabobankDescription'       => 'Select this when you\'re importing Rabobank CSV export files.',
    +    'csv_specifix_Dummy'                     => 'Checking this has no effect whatsoever.',
    +    'csv_import_account_help'                => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.',
    +    'csv_date_parse_error'                   => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?',
     
         // create new stuff:
    -    'create_new_withdrawal'            => 'Create new withdrawal',
    -    'create_new_deposit'               => 'Create new deposit',
    -    'create_new_transfer'              => 'Create new transfer',
    -    'create_new_asset'                 => 'Create new asset account',
    -    'create_new_expense'               => 'Create new expense account',
    -    'create_new_revenue'               => 'Create new revenue account',
    -    'create_new_piggy_bank'            => 'Create new piggy bank',
    -    'create_new_bill'                  => 'Create new bill',
    +    'create_new_withdrawal'                  => 'Create new withdrawal',
    +    'create_new_deposit'                     => 'Create new deposit',
    +    'create_new_transfer'                    => 'Create new transfer',
    +    'create_new_asset'                       => 'Create new asset account',
    +    'create_new_expense'                     => 'Create new expense account',
    +    'create_new_revenue'                     => 'Create new revenue account',
    +    'create_new_piggy_bank'                  => 'Create new piggy bank',
    +    'create_new_bill'                        => 'Create new bill',
     
         // currencies:
    -    'create_currency'                  => 'Create a new currency',
    -    'edit_currency'                    => 'Edit currency ":name"',
    -    'store_currency'                   => 'Store new currency',
    -    'update_currency'                  => 'Update currency',
    +    'create_currency'                        => 'Create a new currency',
    +    'edit_currency'                          => 'Edit currency ":name"',
    +    'store_currency'                         => 'Store new currency',
    +    'update_currency'                        => 'Update currency',
     
         // new user:
    -    'submit'                           => 'Submit',
    -    'getting_started'                  => 'Getting started',
    -    'to_get_started'                   => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:',
    -    'savings_balance_text'             => 'If you have a savings account, please enter the current balance of your savings account:',
    -    'cc_balance_text'                  => 'If you have a credit card, please enter your credit card\'s limit.',
    +    'submit'                                 => 'Submit',
    +    'getting_started'                        => 'Getting started',
    +    'to_get_started'                         => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:',
    +    'savings_balance_text'                   => 'If you have a savings account, please enter the current balance of your savings account:',
    +    'cc_balance_text'                        => 'If you have a credit card, please enter your credit card\'s limit.',
     
         // forms:
    -    'mandatoryFields'                  => 'Mandatory fields',
    -    'optionalFields'                   => 'Optional fields',
    -    'options'                          => 'Options',
    -    'something'                        => 'Something!',
    +    'mandatoryFields'                        => 'Mandatory fields',
    +    'optionalFields'                         => 'Optional fields',
    +    'options'                                => 'Options',
    +    'something'                              => 'Something!',
     
         // budgets:
    -    'create_new_budget'                => 'Create a new budget',
    -    'store_new_budget'                 => ' Store new budget',
    -    'availableIn'                      => 'Available in :date',
    -    'transactionsWithoutBudget'        => 'Expenses without budget',
    -    'transactionsWithoutBudgetDate'    => 'Expenses without budget in :date',
    -    'createBudget'                     => 'New budget',
    -    'inactiveBudgets'                  => 'Inactive budgets',
    -    'without_budget_between'           => 'Transactions without a budget between :start and :end',
    -    'budget_in_month'                  => ':name in :month',
    -    'delete_budget'                    => 'Delete budget ":name"',
    -    'edit_budget'                      => 'Edit budget ":name"',
    -    'update_amount'                    => 'Update amount',
    -    'update_budget'                    => 'Update budget',
    +    'create_new_budget'                      => 'Create a new budget',
    +    'store_new_budget'                       => ' Store new budget',
    +    'availableIn'                            => 'Available in :date',
    +    'transactionsWithoutBudget'              => 'Expenses without budget',
    +    'transactionsWithoutBudgetDate'          => 'Expenses without budget in :date',
    +    'createBudget'                           => 'New budget',
    +    'inactiveBudgets'                        => 'Inactive budgets',
    +    'without_budget_between'                 => 'Transactions without a budget between :start and :end',
    +    'budget_in_month'                        => ':name in :month',
    +    'delete_budget'                          => 'Delete budget ":name"',
    +    'edit_budget'                            => 'Edit budget ":name"',
    +    'update_amount'                          => 'Update amount',
    +    'update_budget'                          => 'Update budget',
     
         // bills:
    -    'delete_bill'                      => 'Delete bill ":name"',
    -    'edit_bill'                        => 'Edit bill ":name"',
    -    'update_bill'                      => 'Update bill',
    -    'store_new_bill'                   => 'Store new bill',
    +    'delete_bill'                            => 'Delete bill ":name"',
    +    'edit_bill'                              => 'Edit bill ":name"',
    +    'update_bill'                            => 'Update bill',
    +    'store_new_bill'                         => 'Store new bill',
     
         // accounts:
    -    'details_for_asset'                => 'Details for asset account ":name"',
    -    'details_for_expense'              => 'Details for expense account ":name"',
    -    'details_for_revenue'              => 'Details for revenue account ":name"',
    -    'details_for_cash'                 => 'Details for cash account ":name"',
    -    'store_new_asset_account'          => 'Store new asset account',
    -    'store_new_expense_account'        => 'Store new expense account',
    -    'store_new_revenue_account'        => 'Store new revenue account',
    -    'edit_asset_account'               => 'Edit asset account ":name"',
    -    'edit_expense_account'             => 'Edit expense account ":name"',
    -    'edit_revenue_account'             => 'Edit revenue account ":name"',
    -    'delete_asset_account'             => 'Delete asset account ":name"',
    -    'delete_expense_account'           => 'Delete expense account ":name"',
    -    'delete_revenue_account'           => 'Delete revenue account ":name"',
    -    'asset_deleted'                    => 'Successfully deleted asset account ":name"',
    -    'expense_deleted'                  => 'Successfully deleted expense account ":name"',
    -    'revenue_deleted'                  => 'Successfully deleted revenue account ":name"',
    -    'update_asset_account'             => 'Update asset account',
    -    'update_expense_account'           => 'Update expense account',
    -    'update_revenue_account'           => 'Update revenue account',
    -    'make_new_asset_account'           => 'Create a new asset account',
    -    'make_new_expense_account'         => 'Create a new expense account',
    -    'make_new_revenue_account'         => 'Create a new revenue account',
    -    'asset_accounts'                   => 'Asset accounts',
    -    'expense_accounts'                 => 'Expense accounts',
    -    'revenue_accounts'                 => 'Revenue accounts',
    -    'accountExtraHelp_asset'           => '',
    -    'accountExtraHelp_expense'         => '',
    -    'accountExtraHelp_revenue'         => '',
    -    'account_type'                     => 'Account type',
    -    'save_transactions_by_moving'      => 'Save these transaction(s) by moving them to another account:',
    +    'details_for_asset'                      => 'Details for asset account ":name"',
    +    'details_for_expense'                    => 'Details for expense account ":name"',
    +    'details_for_revenue'                    => 'Details for revenue account ":name"',
    +    'details_for_cash'                       => 'Details for cash account ":name"',
    +    'store_new_asset_account'                => 'Store new asset account',
    +    'store_new_expense_account'              => 'Store new expense account',
    +    'store_new_revenue_account'              => 'Store new revenue account',
    +    'edit_asset_account'                     => 'Edit asset account ":name"',
    +    'edit_expense_account'                   => 'Edit expense account ":name"',
    +    'edit_revenue_account'                   => 'Edit revenue account ":name"',
    +    'delete_asset_account'                   => 'Delete asset account ":name"',
    +    'delete_expense_account'                 => 'Delete expense account ":name"',
    +    'delete_revenue_account'                 => 'Delete revenue account ":name"',
    +    'asset_deleted'                          => 'Successfully deleted asset account ":name"',
    +    'expense_deleted'                        => 'Successfully deleted expense account ":name"',
    +    'revenue_deleted'                        => 'Successfully deleted revenue account ":name"',
    +    'update_asset_account'                   => 'Update asset account',
    +    'update_expense_account'                 => 'Update expense account',
    +    'update_revenue_account'                 => 'Update revenue account',
    +    'make_new_asset_account'                 => 'Create a new asset account',
    +    'make_new_expense_account'               => 'Create a new expense account',
    +    'make_new_revenue_account'               => 'Create a new revenue account',
    +    'asset_accounts'                         => 'Asset accounts',
    +    'expense_accounts'                       => 'Expense accounts',
    +    'revenue_accounts'                       => 'Revenue accounts',
    +    'accountExtraHelp_asset'                 => '',
    +    'accountExtraHelp_expense'               => '',
    +    'accountExtraHelp_revenue'               => '',
    +    'account_type'                           => 'Account type',
    +    'save_transactions_by_moving'            => 'Save these transaction(s) by moving them to another account:',
     
         // categories:
    -    'new_category'                     => 'New category',
    -    'create_new_category'              => 'Create a new category',
    -    'without_category'                 => 'Without a category',
    -    'update_category'                  => 'Wijzig categorie',
    -    'categories'                       => 'Categories',
    -    'edit_category'                    => 'Edit category ":name"',
    -    'no_category'                      => '(no category)',
    -    'category'                         => 'Category',
    -    'delete_category'                  => 'Delete category ":name"',
    -    'store_category'                   => 'Store new category',
    -    'without_category_between'         => 'Without category between :start and :end',
    +    'new_category'                           => 'New category',
    +    'create_new_category'                    => 'Create a new category',
    +    'without_category'                       => 'Without a category',
    +    'update_category'                        => 'Wijzig categorie',
    +    'categories'                             => 'Categories',
    +    'edit_category'                          => 'Edit category ":name"',
    +    'no_category'                            => '(no category)',
    +    'category'                               => 'Category',
    +    'delete_category'                        => 'Delete category ":name"',
    +    'store_category'                         => 'Store new category',
    +    'without_category_between'               => 'Without category between :start and :end',
     
         // transactions:
    -    'update_withdrawal'                => 'Update withdrawal',
    -    'update_deposit'                   => 'Update deposit',
    -    'update_transfer'                  => 'Update transfer',
    -    'delete_withdrawal'                => 'Delete withdrawal ":description"',
    -    'delete_deposit'                   => 'Delete deposit ":description"',
    -    'delete_transfer'                  => 'Delete transfer ":description"',
    +    'update_withdrawal'                      => 'Update withdrawal',
    +    'update_deposit'                         => 'Update deposit',
    +    'update_transfer'                        => 'Update transfer',
    +    'delete_withdrawal'                      => 'Delete withdrawal ":description"',
    +    'delete_deposit'                         => 'Delete deposit ":description"',
    +    'delete_transfer'                        => 'Delete transfer ":description"',
     
         // new user:
    -    'welcome'                          => 'Welcome to Firefly!',
    -    'createNewAsset'                   => 'Create a new asset account to get started. ' .
    -                                          'This will allow you to create transactions and start your financial management',
    -    'createNewAssetButton'             => 'Create new asset account',
    +    'welcome'                                => 'Welcome to Firefly!',
    +    'createNewAsset'                         => 'Create a new asset account to get started. ' .
    +                                                'This will allow you to create transactions and start your financial management',
    +    'createNewAssetButton'                   => 'Create new asset account',
     
         // home page:
    -    'yourAccounts'                     => 'Your accounts',
    -    'budgetsAndSpending'               => 'Budgets and spending',
    -    'savings'                          => 'Savings',
    -    'markAsSavingsToContinue'          => 'Mark your asset accounts as "Savings account" to fill this panel',
    -    'createPiggyToContinue'            => 'Create piggy banks to fill this panel.',
    -    'newWithdrawal'                    => 'New expense',
    -    'newDeposit'                       => 'New deposit',
    -    'newTransfer'                      => 'New transfer',
    -    'moneyIn'                          => 'Money in',
    -    'moneyOut'                         => 'Money out',
    -    'billsToPay'                       => 'Bills to pay',
    -    'billsPaid'                        => 'Bills paid',
    -    'viewDetails'                      => 'View details',
    -    'divided'                          => 'divided',
    -    'toDivide'                         => 'left to divide',
    +    'yourAccounts'                           => 'Your accounts',
    +    'budgetsAndSpending'                     => 'Budgets and spending',
    +    'savings'                                => 'Savings',
    +    'markAsSavingsToContinue'                => 'Mark your asset accounts as "Savings account" to fill this panel',
    +    'createPiggyToContinue'                  => 'Create piggy banks to fill this panel.',
    +    'newWithdrawal'                          => 'New expense',
    +    'newDeposit'                             => 'New deposit',
    +    'newTransfer'                            => 'New transfer',
    +    'moneyIn'                                => 'Money in',
    +    'moneyOut'                               => 'Money out',
    +    'billsToPay'                             => 'Bills to pay',
    +    'billsPaid'                              => 'Bills paid',
    +    'viewDetails'                            => 'View details',
    +    'divided'                                => 'divided',
    +    'toDivide'                               => 'left to divide',
     
         // menu and titles, should be recycled as often as possible:
    -    'toggleNavigation'                 => 'Toggle navigation',
    -    'currency'                         => 'Currency',
    -    'preferences'                      => 'Preferences',
    -    'logout'                           => 'Logout',
    -    'searchPlaceholder'                => 'Search...',
    -    'dashboard'                        => 'Dashboard',
    -    'currencies'                       => 'Currencies',
    -    'accounts'                         => 'Accounts',
    -    'Asset account'                    => 'Asset account',
    -    'Default account'                  => 'Asset account',
    -    'Expense account'                  => 'Expense account',
    -    'Revenue account'                  => 'Revenue account',
    -    'Initial balance account'          => 'Initial balance account',
    -    'budgets'                          => 'Budgets',
    -    'tags'                             => 'Tags',
    -    'reports'                          => 'Reports',
    -    'transactions'                     => 'Transactions',
    -    'expenses'                         => 'Expenses',
    -    'income'                           => 'Revenue / income',
    -    'transfers'                        => 'Transfers',
    -    'moneyManagement'                  => 'Money management',
    -    'piggyBanks'                       => 'Piggy banks',
    -    'bills'                            => 'Bills',
    -    'createNew'                        => 'Create new',
    -    'withdrawal'                       => 'Withdrawal',
    -    'deposit'                          => 'Deposit',
    -    'account'                          => 'Account',
    -    'transfer'                         => 'Transfer',
    -    'Withdrawal'                       => 'Withdrawal',
    -    'Deposit'                          => 'Deposit',
    -    'Transfer'                         => 'Transfer',
    -    'bill'                             => 'Bill',
    -    'yes'                              => 'Yes',
    -    'no'                               => 'No',
    -    'amount'                           => 'Amount',
    -    'newBalance'                       => 'New balance',
    -    'overview'                         => 'Overview',
    -    'saveOnAccount'                    => 'Save on account',
    -    'unknown'                          => 'Unknown',
    -    'daily'                            => 'Daily',
    -    'weekly'                           => 'Weekly',
    -    'monthly'                          => 'Monthly',
    -    'quarterly'                        => 'Quarterly',
    -    'half-year'                        => 'Every six months',
    -    'yearly'                           => 'Yearly',
    -    'profile'                          => 'Profile',
    +    'toggleNavigation'                       => 'Toggle navigation',
    +    'currency'                               => 'Currency',
    +    'preferences'                            => 'Preferences',
    +    'logout'                                 => 'Logout',
    +    'searchPlaceholder'                      => 'Search...',
    +    'dashboard'                              => 'Dashboard',
    +    'currencies'                             => 'Currencies',
    +    'accounts'                               => 'Accounts',
    +    'Asset account'                          => 'Asset account',
    +    'Default account'                        => 'Asset account',
    +    'Expense account'                        => 'Expense account',
    +    'Revenue account'                        => 'Revenue account',
    +    'Initial balance account'                => 'Initial balance account',
    +    'budgets'                                => 'Budgets',
    +    'tags'                                   => 'Tags',
    +    'reports'                                => 'Reports',
    +    'transactions'                           => 'Transactions',
    +    'expenses'                               => 'Expenses',
    +    'income'                                 => 'Revenue / income',
    +    'transfers'                              => 'Transfers',
    +    'moneyManagement'                        => 'Money management',
    +    'piggyBanks'                             => 'Piggy banks',
    +    'bills'                                  => 'Bills',
    +    'createNew'                              => 'Create new',
    +    'withdrawal'                             => 'Withdrawal',
    +    'deposit'                                => 'Deposit',
    +    'account'                                => 'Account',
    +    'transfer'                               => 'Transfer',
    +    'Withdrawal'                             => 'Withdrawal',
    +    'Deposit'                                => 'Deposit',
    +    'Transfer'                               => 'Transfer',
    +    'bill'                                   => 'Bill',
    +    'yes'                                    => 'Yes',
    +    'no'                                     => 'No',
    +    'amount'                                 => 'Amount',
    +    'newBalance'                             => 'New balance',
    +    'overview'                               => 'Overview',
    +    'saveOnAccount'                          => 'Save on account',
    +    'unknown'                                => 'Unknown',
    +    'daily'                                  => 'Daily',
    +    'weekly'                                 => 'Weekly',
    +    'monthly'                                => 'Monthly',
    +    'quarterly'                              => 'Quarterly',
    +    'half-year'                              => 'Every six months',
    +    'yearly'                                 => 'Yearly',
    +    'profile'                                => 'Profile',
     
         // reports:
    -    'report_default'                   => 'Default financial report for :start until :end',
    -    'quick_link_reports'               => 'Quick links',
    -    'quick_link_default_report'        => 'Default financial report',
    -    'report_this_month_quick'          => 'Current month, all accounts',
    -    'report_this_year_quick'           => 'Current year, all accounts',
    -    'report_all_time_quick'            => 'All-time, all accounts',
    -    'reports_can_bookmark'             => 'Remember that reports can be bookmarked.',
    -    'incomeVsExpenses'                 => 'Income vs. expenses',
    -    'accountBalances'                  => 'Account balances',
    -    'balanceStartOfYear'               => 'Balance at start of year',
    -    'balanceEndOfYear'                 => 'Balance at end of year',
    -    'balanceStartOfMonth'              => 'Balance at start of month',
    -    'balanceEndOfMonth'                => 'Balance at end of month',
    -    'balanceStart'                     => 'Balance at start of period',
    -    'balanceEnd'                       => 'Balance at end of period',
    -    'reportsOwnAccounts'               => 'Reports for your own accounts',
    -    'reportsOwnAccountsAndShared'      => 'Reports for your own accounts and shared accounts',
    -    'splitByAccount'                   => 'Split by account',
    -    'balancedByTransfersAndTags'       => 'Balanced by transfers and tags',
    -    'coveredWithTags'                  => 'Covered with tags',
    -    'leftUnbalanced'                   => 'Left unbalanced',
    -    'expectedBalance'                  => 'Expected balance',
    -    'outsideOfBudgets'                 => 'Outside of budgets',
    -    'leftInBudget'                     => 'Left in budget',
    -    'sumOfSums'                        => 'Sum of sums',
    -    'noCategory'                       => '(no category)',
    -    'notCharged'                       => 'Not charged (yet)',
    -    'inactive'                         => 'Inactive',
    -    'difference'                       => 'Difference',
    -    'in'                               => 'In',
    -    'out'                              => 'Out',
    -    'topX'                             => 'top :number',
    -    'showTheRest'                      => 'Show everything',
    -    'hideTheRest'                      => 'Show only the top :number',
    -    'sum_of_year'                      => 'Sum of year',
    -    'sum_of_years'                     => 'Sum of years',
    -    'average_of_year'                  => 'Average of year',
    -    'average_of_years'                 => 'Average of years',
    -    'categories_earned_in_year'        => 'Categories (by earnings)',
    -    'categories_spent_in_year'         => 'Categories (by spendings)',
    -    'report_type'                      => 'Report type',
    -    'report_type_default'              => 'Default financial report',
    -    'report_included_accounts'         => 'Included accounts',
    -    'report_date_range'                => 'Date range',
    -    'report_include_help'              => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.',
    -    'report_preset_ranges'             => 'Pre-set ranges',
    -    'shared'                           => 'Shared',
    +    'report_default'                         => 'Default financial report for :start until :end',
    +    'quick_link_reports'                     => 'Quick links',
    +    'quick_link_default_report'              => 'Default financial report',
    +    'report_this_month_quick'                => 'Current month, all accounts',
    +    'report_this_year_quick'                 => 'Current year, all accounts',
    +    'report_all_time_quick'                  => 'All-time, all accounts',
    +    'reports_can_bookmark'                   => 'Remember that reports can be bookmarked.',
    +    'incomeVsExpenses'                       => 'Income vs. expenses',
    +    'accountBalances'                        => 'Account balances',
    +    'balanceStartOfYear'                     => 'Balance at start of year',
    +    'balanceEndOfYear'                       => 'Balance at end of year',
    +    'balanceStartOfMonth'                    => 'Balance at start of month',
    +    'balanceEndOfMonth'                      => 'Balance at end of month',
    +    'balanceStart'                           => 'Balance at start of period',
    +    'balanceEnd'                             => 'Balance at end of period',
    +    'reportsOwnAccounts'                     => 'Reports for your own accounts',
    +    'reportsOwnAccountsAndShared'            => 'Reports for your own accounts and shared accounts',
    +    'splitByAccount'                         => 'Split by account',
    +    'balancedByTransfersAndTags'             => 'Balanced by transfers and tags',
    +    'coveredWithTags'                        => 'Covered with tags',
    +    'leftUnbalanced'                         => 'Left unbalanced',
    +    'expectedBalance'                        => 'Expected balance',
    +    'outsideOfBudgets'                       => 'Outside of budgets',
    +    'leftInBudget'                           => 'Left in budget',
    +    'sumOfSums'                              => 'Sum of sums',
    +    'noCategory'                             => '(no category)',
    +    'notCharged'                             => 'Not charged (yet)',
    +    'inactive'                               => 'Inactive',
    +    'difference'                             => 'Difference',
    +    'in'                                     => 'In',
    +    'out'                                    => 'Out',
    +    'topX'                                   => 'top :number',
    +    'showTheRest'                            => 'Show everything',
    +    'hideTheRest'                            => 'Show only the top :number',
    +    'sum_of_year'                            => 'Sum of year',
    +    'sum_of_years'                           => 'Sum of years',
    +    'average_of_year'                        => 'Average of year',
    +    'average_of_years'                       => 'Average of years',
    +    'categories_earned_in_year'              => 'Categories (by earnings)',
    +    'categories_spent_in_year'               => 'Categories (by spendings)',
    +    'report_type'                            => 'Report type',
    +    'report_type_default'                    => 'Default financial report',
    +    'report_included_accounts'               => 'Included accounts',
    +    'report_date_range'                      => 'Date range',
    +    'report_include_help'                    => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.',
    +    'report_preset_ranges'                   => 'Pre-set ranges',
    +    'shared'                                 => 'Shared',
     
         // charts:
    -    'dayOfMonth'                       => 'Day of the month',
    -    'month'                            => 'Month',
    -    'budget'                           => 'Budget',
    -    'spent'                            => 'Spent',
    -    'earned'                           => 'Earned',
    -    'overspent'                        => 'Overspent',
    -    'left'                             => 'Left',
    -    'noBudget'                         => '(no budget)',
    -    'maxAmount'                        => 'Maximum amount',
    -    'minAmount'                        => 'Minumum amount',
    -    'billEntry'                        => 'Current bill entry',
    -    'name'                             => 'Name',
    -    'date'                             => 'Date',
    -    'paid'                             => 'Paid',
    -    'unpaid'                           => 'Unpaid',
    -    'day'                              => 'Day',
    -    'budgeted'                         => 'Budgeted',
    -    'period'                           => 'Period',
    -    'balance'                          => 'Balance',
    -    'summary'                          => 'Summary',
    -    'sum'                              => 'Sum',
    -    'average'                          => 'Average',
    -    'balanceFor'                       => 'Balance for :name',
    +    'dayOfMonth'                             => 'Day of the month',
    +    'month'                                  => 'Month',
    +    'budget'                                 => 'Budget',
    +    'spent'                                  => 'Spent',
    +    'earned'                                 => 'Earned',
    +    'overspent'                              => 'Overspent',
    +    'left'                                   => 'Left',
    +    'noBudget'                               => '(no budget)',
    +    'maxAmount'                              => 'Maximum amount',
    +    'minAmount'                              => 'Minumum amount',
    +    'billEntry'                              => 'Current bill entry',
    +    'name'                                   => 'Name',
    +    'date'                                   => 'Date',
    +    'paid'                                   => 'Paid',
    +    'unpaid'                                 => 'Unpaid',
    +    'day'                                    => 'Day',
    +    'budgeted'                               => 'Budgeted',
    +    'period'                                 => 'Period',
    +    'balance'                                => 'Balance',
    +    'summary'                                => 'Summary',
    +    'sum'                                    => 'Sum',
    +    'average'                                => 'Average',
    +    'balanceFor'                             => 'Balance for :name',
     
         // piggy banks:
    -    'piggy_bank'                       => 'Piggy bank',
    -    'new_piggy_bank'                   => 'Create new piggy bank',
    -    'store_piggy_bank'                 => 'Store new piggy bank',
    -    'account_status'                   => 'Account status',
    -    'left_for_piggy_banks'             => 'Left for piggy banks',
    -    'sum_of_piggy_banks'               => 'Sum of piggy banks',
    -    'saved_so_far'                     => 'Saved so far',
    -    'left_to_save'                     => 'Left to save',
    -    'add_money_to_piggy_title'         => 'Add money to piggy bank ":name"',
    -    'remove_money_from_piggy_title'    => 'Remove money from piggy bank ":name"',
    -    'add'                              => 'Add',
    -    'remove'                           => 'Remove',
    -    'max_amount_add'                   => 'The maximum amount you can add is',
    -    'max_amount_remove'                => 'The maximum amount you can remove is',
    -    'update_piggy_button'              => 'Update piggy bank',
    -    'update_piggy_title'               => 'Update piggy bank ":name"',
    -    'details'                          => 'Details',
    -    'events'                           => 'Events',
    -    'target_amount'                    => 'Target amount',
    -    'start_date'                       => 'Start date',
    -    'target_date'                      => 'Target date',
    -    'no_target_date'                   => 'No target date',
    -    'todo'                             => 'to do',
    -    'table'                            => 'Table',
    -    'piggy_bank_not_exists'            => 'Piggy bank no longer exists.',
    -    'add_any_amount_to_piggy'          => 'Add money to this piggy bank to reach your target of :amount.',
    -    'add_set_amount_to_piggy'          => 'Add :amount to fill this piggy bank on :date',
    -    'delete_piggy_bank'                => 'Delete piggy bank ":name"',
    +    'piggy_bank'                             => 'Piggy bank',
    +    'new_piggy_bank'                         => 'Create new piggy bank',
    +    'store_piggy_bank'                       => 'Store new piggy bank',
    +    'account_status'                         => 'Account status',
    +    'left_for_piggy_banks'                   => 'Left for piggy banks',
    +    'sum_of_piggy_banks'                     => 'Sum of piggy banks',
    +    'saved_so_far'                           => 'Saved so far',
    +    'left_to_save'                           => 'Left to save',
    +    'add_money_to_piggy_title'               => 'Add money to piggy bank ":name"',
    +    'remove_money_from_piggy_title'          => 'Remove money from piggy bank ":name"',
    +    'add'                                    => 'Add',
    +    'remove'                                 => 'Remove',
    +    'max_amount_add'                         => 'The maximum amount you can add is',
    +    'max_amount_remove'                      => 'The maximum amount you can remove is',
    +    'update_piggy_button'                    => 'Update piggy bank',
    +    'update_piggy_title'                     => 'Update piggy bank ":name"',
    +    'details'                                => 'Details',
    +    'events'                                 => 'Events',
    +    'target_amount'                          => 'Target amount',
    +    'start_date'                             => 'Start date',
    +    'target_date'                            => 'Target date',
    +    'no_target_date'                         => 'No target date',
    +    'todo'                                   => 'to do',
    +    'table'                                  => 'Table',
    +    'piggy_bank_not_exists'                  => 'Piggy bank no longer exists.',
    +    'add_any_amount_to_piggy'                => 'Add money to this piggy bank to reach your target of :amount.',
    +    'add_set_amount_to_piggy'                => 'Add :amount to fill this piggy bank on :date',
    +    'delete_piggy_bank'                      => 'Delete piggy bank ":name"',
     
         // tags
    -    'regular_tag'                      => 'Just a regular tag.',
    -    'balancing_act'                    => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
    -    'advance_payment'                  => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
    -    'delete_tag'                       => 'Delete tag ":tag"',
    -    'new_tag'                          => 'Make new tag',
    -    'edit_tag'                         => 'Edit tag ":tag"',
    -    'no_year'                          => 'No year set',
    -    'no_month'                         => 'No month set',
    -    'tag_title_nothing'                => 'Default tags',
    -    'tag_title_balancingAct'           => 'Balancing act tags',
    -    'tag_title_advancePayment'         => 'Advance payment tags',
    -    'tags_introduction'                => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called  Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
    -    'tags_group'                       => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
    -    'tags_start'                       => 'Create a tag to get started or enter tags when creating new transactions.',
    +    'regular_tag'                            => 'Just a regular tag.',
    +    'balancing_act'                          => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
    +    'advance_payment'                        => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
    +    'delete_tag'                             => 'Delete tag ":tag"',
    +    'new_tag'                                => 'Make new tag',
    +    'edit_tag'                               => 'Edit tag ":tag"',
    +    'no_year'                                => 'No year set',
    +    'no_month'                               => 'No month set',
    +    'tag_title_nothing'                      => 'Default tags',
    +    'tag_title_balancingAct'                 => 'Balancing act tags',
    +    'tag_title_advancePayment'               => 'Advance payment tags',
    +    'tags_introduction'                      => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called  Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
    +    'tags_group'                             => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
    +    'tags_start'                             => 'Create a tag to get started or enter tags when creating new transactions.',
     
     ];
    diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php
    index 3ff3349a6b..a8d6c60f32 100644
    --- a/resources/lang/en_US/form.php
    +++ b/resources/lang/en_US/form.php
    @@ -67,6 +67,7 @@ return [
         'filename'                    => 'File name',
         'mime'                        => 'Mime type',
         'size'                        => 'Size',
    +    'trigger'                     => 'Trigger',
         'stop_processing'             => 'Stop processing',
     
     
    diff --git a/resources/views/rules/partials/action.twig b/resources/views/rules/partials/action.twig
    new file mode 100644
    index 0000000000..685bdb4304
    --- /dev/null
    +++ b/resources/views/rules/partials/action.twig
    @@ -0,0 +1,22 @@
    +
    +    
    +        
    +    
    +    
    +        
    +    
    +    
    +        
    +    
    +    
    +        
    + +
    + + \ No newline at end of file diff --git a/resources/views/rules/partials/trigger.twig b/resources/views/rules/partials/trigger.twig new file mode 100644 index 0000000000..f401825ae1 --- /dev/null +++ b/resources/views/rules/partials/trigger.twig @@ -0,0 +1,22 @@ + + + + + + + + + + + +
    + +
    + + \ No newline at end of file diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index 64534e0b71..b52537f508 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -15,6 +15,7 @@
    {{ ExpandedForm.text('title') }} + {{ ExpandedForm.select('trigger',journalTriggers) }} {{ ExpandedForm.checkbox('stop_processing',1,null, {helpText: trans('firefly.rule_help_stop_processing')}) }}
    @@ -40,8 +41,23 @@

    {{ 'rule_triggers'|_ }}

    -
    - Here +
    + + + + + + + + + + + +
    {{ 'trigger'|_ }}{{ 'trigger_value'|_ }}{{ 'stop_processing_other_triggers'|_ }}
    +

    +
    + {{ 'add_rule_trigger'|_ }} +

    @@ -55,7 +71,22 @@

    {{ 'rule_actions'|_ }}

    - Here + + + + + + + + + + + +
    {{ 'action'|_ }}{{ 'action_value'|_ }}{{ 'stop_executing_other_actions'|_ }}
    +

    +
    + {{ 'add_rule_action'|_ }} +

    @@ -83,3 +114,7 @@ {% endblock %} +{% block scripts %} + + +{% endblock %} From cd60b852a18c48df8654cab9144e9cafaf96f258 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 18:09:20 +0100 Subject: [PATCH 130/276] This isn't really working. --- app/Http/Controllers/RuleController.php | 54 ++++++++++++++++++-- app/Http/Requests/RuleFormRequest.php | 55 +++++++++++++++++++++ resources/views/rules/partials/action.twig | 7 ++- resources/views/rules/partials/trigger.twig | 17 +++++-- resources/views/rules/rule/create.twig | 14 +++++- 5 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 app/Http/Requests/RuleFormRequest.php diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 0070aa6ed7..e70d5d07e5 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -10,8 +10,10 @@ namespace FireflyIII\Http\Controllers; use Auth; +use Config; use ExpandedForm; use FireflyIII\Http\Requests; +use FireflyIII\Http\Requests\RuleFormRequest; use FireflyIII\Http\Requests\RuleGroupFormRequest; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; @@ -64,10 +66,11 @@ class RuleController extends Controller * * @return View */ - public function storeRule(RuleGroup $ruleGroup) + public function storeRule(RuleFormRequest $request, RuleGroup $ruleGroup) { echo '
    ';
    -        var_dump(Input::all());exit();
    +        var_dump(Input::all());
    +        exit();
         }
     
         /**
    @@ -77,6 +80,51 @@ class RuleController extends Controller
          */
         public function createRule(RuleGroup $ruleGroup)
         {
    +        // count for possible present previous entered triggers/actions.
    +        $triggerCount = 0;
    +        $actionCount  = 0;
    +
    +        // collection of those triggers/actions.
    +        $oldTriggers = [];
    +        $oldActions  = [];
    +
    +        // array of valid values for triggers.
    +        $ruleTriggers     = array_keys(Config::get('firefly.rule-triggers'));
    +        $possibleTriggers = [];
    +        foreach ($ruleTriggers as $key) {
    +            if ($key != 'user_action') {
    +                $possibleTriggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice');
    +            }
    +        }
    +        unset($key, $ruleTriggers);
    +
    +        // has old input?
    +        if (Input::old()) {
    +            // process old triggers.
    +            foreach (Input::old('rule-trigger') as $index => $entry) {
    +                $count = ($index + 1);
    +                $triggerCount++;
    +                $oldTrigger    = $entry;
    +                $oldValue      = Input::old('rule-trigger-value')[$index];
    +                $oldChecked    = isset(Input::old('rule-action-value')[$index]) ? true : false;
    +                $oldTriggers[] = view(
    +                    'rules.partials.trigger',
    +                    [
    +                        'oldTrigger' => $oldTrigger,
    +                        'oldValue'   => $oldValue,
    +                        'oldChecked' => $oldChecked,
    +                        'triggers'   => $possibleTriggers,
    +                        'count'      => $count
    +                    ]
    +                )->render();
    +            }
    +//            echo '
    ';
    +//            var_dump(Input::old());
    +//            var_dump($oldTriggers);
    +//            exit;
    +        }
    +
    +
             $subTitleIcon = 'fa-clone';
             $subTitle     = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]);
     
    @@ -95,7 +143,7 @@ class RuleController extends Controller
             Session::flash('gaEventCategory', 'rules');
             Session::flash('gaEventAction', 'create-rule-group');
     
    -        return view('rules.rule.create', compact('subTitleIcon', 'ruleGroup', 'subTitle', 'journalTriggers'));
    +        return view('rules.rule.create', compact('subTitleIcon','oldTriggers', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle', 'journalTriggers'));
         }
     
         /**
    diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php
    new file mode 100644
    index 0000000000..181f573a8c
    --- /dev/null
    +++ b/app/Http/Requests/RuleFormRequest.php
    @@ -0,0 +1,55 @@
    + $titleRule,
    +            'description'     => 'between:1,5000',
    +            'stop_processing' => 'boolean',
    +            'trigger'         => 'required|in:store-journal,update-journal',
    +            'rule-trigger.*'  => 'required|in:' . join(',', $validTriggers)
    +        ];
    +    }
    +}
    diff --git a/resources/views/rules/partials/action.twig b/resources/views/rules/partials/action.twig
    index 685bdb4304..edb4d77bf5 100644
    --- a/resources/views/rules/partials/action.twig
    +++ b/resources/views/rules/partials/action.twig
    @@ -3,9 +3,14 @@
             
         
         
    +        {% if errors.has('XXX') %}
    +            
    +            

    {{ errors.first('xxxx') }}

    + {% endif %} + diff --git a/resources/views/rules/partials/trigger.twig b/resources/views/rules/partials/trigger.twig index f401825ae1..d74d993ab5 100644 --- a/resources/views/rules/partials/trigger.twig +++ b/resources/views/rules/partials/trigger.twig @@ -3,19 +3,30 @@ + {% if errors.has('rule-trigger.'~count) %} + +

    {{ errors.first('rule-trigger.'~count) }}

    + {% endif %} + - +
    diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index b52537f508..c1bee36cd2 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -41,6 +41,10 @@

    {{ 'rule_triggers'|_ }}

    + + {% if errors.has('rule-trigger.1') %} + {{ errors.first('rule-trigger.1') }} + {% endif %}
    @@ -51,6 +55,9 @@ + {% for trigger in oldTriggers %} + {{ trigger|raw }} + {% endfor %}
    @@ -80,6 +87,7 @@ + @@ -93,7 +101,7 @@
    -
    +
    @@ -116,5 +124,9 @@ {% endblock %} {% block scripts %} + {% endblock %} From 42203ba872cbb05eff1f8450731c1e452ad49784 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 18:57:52 +0100 Subject: [PATCH 131/276] Attempt at validating. --- app/Http/Controllers/RuleController.php | 4 +++- app/Http/Requests/RuleFormRequest.php | 5 ++++- public/js/rules/{edit.js => create.js} | 5 ++++- resources/views/rules/partials/trigger.twig | 9 ++++++++- resources/views/rules/rule/create.twig | 5 +---- 5 files changed, 20 insertions(+), 8 deletions(-) rename public/js/rules/{edit.js => create.js} (88%) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index e70d5d07e5..f6410c7894 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -101,8 +101,9 @@ class RuleController extends Controller // has old input? if (Input::old()) { // process old triggers. + $newIndex = 0; foreach (Input::old('rule-trigger') as $index => $entry) { - $count = ($index + 1); + $count = ($newIndex + 1); $triggerCount++; $oldTrigger = $entry; $oldValue = Input::old('rule-trigger-value')[$index]; @@ -117,6 +118,7 @@ class RuleController extends Controller 'count' => $count ] )->render(); + $newIndex++; } // echo '
    ';
     //            var_dump(Input::old());
    diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php
    index 181f573a8c..521ecf3d43 100644
    --- a/app/Http/Requests/RuleFormRequest.php
    +++ b/app/Http/Requests/RuleFormRequest.php
    @@ -49,7 +49,10 @@ class RuleFormRequest extends Request
                 'description'     => 'between:1,5000',
                 'stop_processing' => 'boolean',
                 'trigger'         => 'required|in:store-journal,update-journal',
    -            'rule-trigger.*'  => 'required|in:' . join(',', $validTriggers)
    +            'rule-trigger.*'  => 'required|in:' . join(',', $validTriggers),
    +            'rule-trigger-value.*'  => 'required|min:1'
    +
    +            
             ];
         }
     }
    diff --git a/public/js/rules/edit.js b/public/js/rules/create.js
    similarity index 88%
    rename from public/js/rules/edit.js
    rename to public/js/rules/create.js
    index 581ece4714..9182c31001 100644
    --- a/public/js/rules/edit.js
    +++ b/public/js/rules/create.js
    @@ -11,7 +11,10 @@
     $(function () {
         "use strict";
         console.log("edit");
    -    addNewTrigger();
    +    if (triggerCount == 0) {
    +        addNewTrigger();
    +    }
    +
         addNewAction();
         $('.add_rule_trigger').click(function () {
             addNewTrigger();
    diff --git a/resources/views/rules/partials/trigger.twig b/resources/views/rules/partials/trigger.twig
    index d74d993ab5..20b5d86a04 100644
    --- a/resources/views/rules/partials/trigger.twig
    +++ b/resources/views/rules/partials/trigger.twig
    @@ -19,7 +19,14 @@
             
         
         
    -        
    +
    +        
    +        {% if errors.has(('rule-trigger-value.'~count)) %}
    +        

    + {{ errors.first('rule-trigger-value.'~count) }} +

    + {% endif %}
    diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index c1bee36cd2..5a35d69925 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -42,9 +42,6 @@

    {{ 'rule_triggers'|_ }}

    - {% if errors.has('rule-trigger.1') %} - {{ errors.first('rule-trigger.1') }} - {% endif %}
    @@ -128,5 +125,5 @@ var triggerCount = {{ triggerCount }}; var actionCount = {{ actionCount }}; - + {% endblock %} From 9703439a4c22ce6a0aa02e9239cba278c277faa3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 19:20:02 +0100 Subject: [PATCH 132/276] Some more rule things before I merge a change to the CSV importer. --- app/Http/Controllers/RuleController.php | 43 ++++++++++++++++++---- app/Http/Requests/RuleFormRequest.php | 23 +++++++----- config/firefly.php | 14 ++++++- public/js/rules/create.js | 5 ++- resources/views/rules/partials/action.twig | 23 +++++++++--- resources/views/rules/rule/create.twig | 4 +- 6 files changed, 86 insertions(+), 26 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index f6410c7894..f378bc448d 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -98,6 +98,15 @@ class RuleController extends Controller } unset($key, $ruleTriggers); + // array of valid values for actions + $ruleActions = array_keys(Config::get('firefly.rule-actions')); + $possibleActions = []; + foreach ($ruleActions as $key) { + $possibleActions[$key] = trans('firefly.rule_action_' . $key . '_choice'); + } + unset($key, $ruleActions); + + // has old input? if (Input::old()) { // process old triggers. @@ -107,7 +116,7 @@ class RuleController extends Controller $triggerCount++; $oldTrigger = $entry; $oldValue = Input::old('rule-trigger-value')[$index]; - $oldChecked = isset(Input::old('rule-action-value')[$index]) ? true : false; + $oldChecked = isset(Input::old('rule-trigger-stop')[$index]) ? true : false; $oldTriggers[] = view( 'rules.partials.trigger', [ @@ -120,12 +129,28 @@ class RuleController extends Controller )->render(); $newIndex++; } -// echo '
    ';
    -//            var_dump(Input::old());
    -//            var_dump($oldTriggers);
    -//            exit;
    -        }
     
    +            // process old actions
    +            $newIndex = 0;
    +            foreach (Input::old('rule-action') as $index => $entry) {
    +                $count = ($newIndex + 1);
    +                $actionCount++;
    +                $oldAction    = $entry;
    +                $oldValue     = Input::old('rule-action-value')[$index];
    +                $oldChecked   = isset(Input::old('rule-action-stop')[$index]) ? true : false;
    +                $oldActions[] = view(
    +                    'rules.partials.action',
    +                    [
    +                        'oldTrigger' => $oldAction,
    +                        'oldValue'   => $oldValue,
    +                        'oldChecked' => $oldChecked,
    +                        'actions'    => $possibleActions,
    +                        'count'      => $count
    +                    ]
    +                )->render();
    +                $newIndex++;
    +            }
    +        }
     
             $subTitleIcon = 'fa-clone';
             $subTitle     = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]);
    @@ -143,9 +168,11 @@ class RuleController extends Controller
             }
             Session::forget('rules.rule.create.fromStore');
             Session::flash('gaEventCategory', 'rules');
    -        Session::flash('gaEventAction', 'create-rule-group');
    +        Session::flash('gaEventAction', 'create-rule');
     
    -        return view('rules.rule.create', compact('subTitleIcon','oldTriggers', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle', 'journalTriggers'));
    +        return view(
    +            'rules.rule.create', compact('subTitleIcon', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle', 'journalTriggers')
    +        );
         }
     
         /**
    diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php
    index 521ecf3d43..e8f18055ab 100644
    --- a/app/Http/Requests/RuleFormRequest.php
    +++ b/app/Http/Requests/RuleFormRequest.php
    @@ -38,21 +38,26 @@ class RuleFormRequest extends Request
         {
     
             $validTriggers = array_keys(Config::get('firefly.rule-triggers'));
    +        $validActions  = array_keys(Config::get('firefly.rule-actions'));
    +
    +        // some actions require text:
    +        $contextActions = Config::get('firefly.rule-actions-text');
     
             $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
             if (RuleGroup::find(Input::get('id'))) {
                 $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval(Input::get('id'));
             }
     
    -        return [
    -            'title'           => $titleRule,
    -            'description'     => 'between:1,5000',
    -            'stop_processing' => 'boolean',
    -            'trigger'         => 'required|in:store-journal,update-journal',
    -            'rule-trigger.*'  => 'required|in:' . join(',', $validTriggers),
    -            'rule-trigger-value.*'  => 'required|min:1'
    -
    -            
    +        $rules = [
    +            'title'                => $titleRule,
    +            'description'          => 'between:1,5000',
    +            'stop_processing'      => 'boolean',
    +            'trigger'              => 'required|in:store-journal,update-journal',
    +            'rule-trigger.*'       => 'required|in:' . join(',', $validTriggers),
    +            'rule-trigger-value.*' => 'required|min:1',
    +            'rule-action.*'        => 'required|in:' . join(',', $validActions),
    +            'rule-action-value.*'  => 'required_if:rule-action.*,' . join(',', $contextActions)
             ];
    +
         }
     }
    diff --git a/config/firefly.php b/config/firefly.php
    index ddef1a0805..952f9463e6 100644
    --- a/config/firefly.php
    +++ b/config/firefly.php
    @@ -170,7 +170,7 @@ return [
             'end_date'        => 'FireflyIII\Support\Binder\Date'
         ],
     
    -    'rule-triggers' => [
    +    'rule-triggers'     => [
             'user_action'           => 'FireflyIII\Rules\Triggers\UserAction',
             'from_account_starts'   => 'FireflyIII\Rules\Triggers\FromAccountStarts',
             'from_account_ends'     => 'FireflyIII\Rules\Triggers\FromAccountEnds',
    @@ -189,7 +189,7 @@ return [
             'description_contains'  => 'FireflyIII\Rules\Triggers\DescriptionContains',
             'description_is'        => 'FireflyIII\Rules\Triggers\DescriptionIs',
         ],
    -    'rule-actions'  => [
    +    'rule-actions'      => [
             'set_category'        => 'FireflyIII\Rules\Actions\SetCategory',
             'clear_category'      => 'FireflyIII\Rules\Actions\ClearCategory',
             'set_budget'          => 'FireflyIII\Rules\Actions\SetBudget',
    @@ -201,5 +201,15 @@ return [
             'append_description'  => 'FireflyIII\Rules\Actions\AppendDescription',
             'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription',
         ],
    +    // all rule actions that require text input:
    +    'rule-actions-text' => [
    +        'set_category',
    +        'set_budget',
    +        'add_tag',
    +        'remove_tag',
    +        'set_description',
    +        'append_description',
    +        'prepend_description',
    +    ]
     
     ];
    diff --git a/public/js/rules/create.js b/public/js/rules/create.js
    index 9182c31001..5fe38cb550 100644
    --- a/public/js/rules/create.js
    +++ b/public/js/rules/create.js
    @@ -14,8 +14,11 @@ $(function () {
         if (triggerCount == 0) {
             addNewTrigger();
         }
    +    if (actionCount == 0) {
    +        addNewAction();
    +    }
    +
     
    -    addNewAction();
         $('.add_rule_trigger').click(function () {
             addNewTrigger();
     
    diff --git a/resources/views/rules/partials/action.twig b/resources/views/rules/partials/action.twig
    index edb4d77bf5..e7ec792c79 100644
    --- a/resources/views/rules/partials/action.twig
    +++ b/resources/views/rules/partials/action.twig
    @@ -3,24 +3,37 @@
             
         
         
    diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index 5a35d69925..8c907753f4 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -84,7 +84,9 @@ - + {% for action in oldActions %} + {{ action|raw }} + {% endfor %}
    - {% if errors.has('XXX') %} + {% if errors.has('rule-action.'~count) %} -

    {{ errors.first('xxxx') }}

    +

    {{ errors.first('rule-action.'~count) }}

    {% endif %}
    - + + + {% if errors.has(('rule-action-value.'~count)) %} +

    + {{ errors.first('rule-action-value.'~count) }} +

    + {% endif %}
    From 1f538be16ea83e13429686cb00f83bb9eeac583f Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 14 Jan 2016 21:34:17 +0100 Subject: [PATCH 133/276] More code for rules. --- app/Http/Controllers/RuleController.php | 85 +++++++++-- app/Http/Requests/RuleFormRequest.php | 11 +- app/Repositories/Rule/RuleRepository.php | 132 +++++++++++++++++- .../Rule/RuleRepositoryInterface.php | 48 ++++++- app/Validation/FireflyValidator.php | 68 ++++++++- public/js/rules/create-edit.js | 2 +- resources/lang/en_US/firefly.php | 2 + resources/lang/en_US/form.php | 4 +- resources/lang/en_US/validation.php | 2 + resources/views/rules/rule/delete.twig | 33 +++++ 10 files changed, 371 insertions(+), 16 deletions(-) create mode 100644 resources/views/rules/rule/delete.twig diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index f378bc448d..92f1a8e152 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -62,15 +62,45 @@ class RuleController extends Controller } /** - * @param RuleGroup $ruleGroup + * @param RuleFormRequest $request + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup * - * @return View + * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function storeRule(RuleFormRequest $request, RuleGroup $ruleGroup) + public function storeRule(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup) { - echo '
    ';
    -        var_dump(Input::all());
    -        exit();
    +
    +
    +        // process the rule itself:
    +        $data = [
    +            'rule_group_id'       => intval($request->get('rule_group_id')),
    +            'title'               => $request->get('title'),
    +            'trigger'             => $request->get('trigger'),
    +            'description'         => $request->get('description'),
    +            'rule-triggers'       => $request->get('rule-trigger'),
    +            'rule-trigger-values' => $request->get('rule-trigger-value'),
    +            'rule-trigger-stop'   => $request->get('rule-trigger-stop'),
    +            'rule-actions'        => $request->get('rule-action'),
    +            'rule-action-values'  => $request->get('rule-action-value'),
    +            'rule-action-stop'    => $request->get('rule-action-stop'),
    +            'stop_processing'     => $request->get('stop_processing')
    +        ];
    +
    +        $rule = $repository->storeRule($data);
    +        Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title]));
    +        Preferences::mark();
    +
    +        if (intval(Input::get('create_another')) === 1) {
    +            // set value so create routine will not overwrite URL:
    +            Session::put('rules.rule.create.fromStore', true);
    +
    +            return redirect(route('rules.rule.create', [$request->input('what')]))->withInput();
    +        }
    +
    +        // redirect to previous URL.
    +        return redirect(Session::get('rules.rule.create.url'));
    +
         }
     
         /**
    @@ -260,9 +290,29 @@ class RuleController extends Controller
         }
     
         /**
    -     * @param RuleGroup $budget
    +     * @param RuleRepositoryInterface $repository
    +     * @param Rule                    $rule
          *
    -     * @return \Illuminate\View\View
    +     * @return View
    +     */
    +    public function deleteRule(Rule $rule)
    +    {
    +        $subTitle = trans('firefly.delete_rule', ['title' => $rule->title]);
    +
    +        // put previous url in session
    +        Session::put('rules.rule.delete.url', URL::previous());
    +        Session::flash('gaEventCategory', 'rules');
    +        Session::flash('gaEventAction', 'delete-rule');
    +
    +        return view('rules.rule.delete', compact('rule', 'subTitle'));
    +    }
    +
    +
    +    /**
    +     * @param RuleRepositoryInterface $repository
    +     * @param RuleGroup               $ruleGroup
    +     *
    +     * @return View
          */
         public function deleteRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup)
         {
    @@ -279,6 +329,25 @@ class RuleController extends Controller
             return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle', 'ruleGroupList'));
         }
     
    +    /**
    +     * @param Rule                    $rule
    +     * @param RuleRepositoryInterface $repository
    +     *
    +     * @return \Illuminate\Http\RedirectResponse
    +     */
    +    public function destroyRule(RuleRepositoryInterface $repository, Rule $rule)
    +    {
    +
    +        $title = $rule->title;
    +        $repository->destroyRule($rule);
    +
    +        Session::flash('success', trans('firefly.deleted_rule', ['title' => $title]));
    +        Preferences::mark();
    +
    +
    +        return redirect(Session::get('rules.rule.delete.url'));
    +    }
    +
         /**
          * @param RuleGroup               $ruleGroup
          * @param RuleRepositoryInterface $repository
    diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php
    index e8f18055ab..be60f0e06a 100644
    --- a/app/Http/Requests/RuleFormRequest.php
    +++ b/app/Http/Requests/RuleFormRequest.php
    @@ -41,7 +41,7 @@ class RuleFormRequest extends Request
             $validActions  = array_keys(Config::get('firefly.rule-actions'));
     
             // some actions require text:
    -        $contextActions = Config::get('firefly.rule-actions-text');
    +        $contextActions = join(',', Config::get('firefly.rule-actions-text'));
     
             $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
             if (RuleGroup::find(Input::get('id'))) {
    @@ -52,12 +52,17 @@ class RuleFormRequest extends Request
                 'title'                => $titleRule,
                 'description'          => 'between:1,5000',
                 'stop_processing'      => 'boolean',
    +            'rule_group_id'        => 'required|belongsToUser:rule_groups',
                 'trigger'              => 'required|in:store-journal,update-journal',
                 'rule-trigger.*'       => 'required|in:' . join(',', $validTriggers),
    -            'rule-trigger-value.*' => 'required|min:1',
    +            'rule-trigger-value.*' => 'required|min:1|ruleTriggerValue',
                 'rule-action.*'        => 'required|in:' . join(',', $validActions),
    -            'rule-action-value.*'  => 'required_if:rule-action.*,' . join(',', $contextActions)
             ];
    +        // since Laravel does not support this stuff yet, here's a trick.
    +        for ($i = 0; $i < 10; $i++) {
    +            $rules['rule-action-value.' . $i] = 'required_if:rule-action.' . $i . ',' . $contextActions . '|ruleActionValue';
    +        }
     
    +        return $rules;
         }
     }
    diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php
    index dbd61abd40..2c5cc8cc5d 100644
    --- a/app/Repositories/Rule/RuleRepository.php
    +++ b/app/Repositories/Rule/RuleRepository.php
    @@ -11,10 +11,10 @@ namespace FireflyIII\Repositories\Rule;
     
     use Auth;
     use FireflyIII\Models\Rule;
    +use FireflyIII\Models\RuleAction;
     use FireflyIII\Models\RuleGroup;
     use FireflyIII\Models\RuleTrigger;
     use Illuminate\Support\Collection;
    -use Log;
     
     /**
      * Class RuleRepository
    @@ -80,6 +80,7 @@ class RuleRepository implements RuleRepositoryInterface
         /**
          * @param Rule  $rule
          * @param array $ids
    +     *
          * @return bool
          */
         public function reorderRuleTriggers(Rule $rule, array $ids)
    @@ -101,6 +102,7 @@ class RuleRepository implements RuleRepositoryInterface
         /**
          * @param Rule  $rule
          * @param array $ids
    +     *
          * @return bool
          */
         public function reorderRuleActions(Rule $rule, array $ids)
    @@ -172,6 +174,7 @@ class RuleRepository implements RuleRepositoryInterface
     
         /**
          * @param Rule $rule
    +     *
          * @return bool
          */
         public function moveRuleUp(Rule $rule)
    @@ -192,6 +195,7 @@ class RuleRepository implements RuleRepositoryInterface
     
         /**
          * @param Rule $rule
    +     *
          * @return bool
          */
         public function moveRuleDown(Rule $rule)
    @@ -234,6 +238,7 @@ class RuleRepository implements RuleRepositoryInterface
     
         /**
          * @param RuleGroup $ruleGroup
    +     *
          * @return bool
          */
         public function moveRuleGroupUp(RuleGroup $ruleGroup)
    @@ -254,6 +259,7 @@ class RuleRepository implements RuleRepositoryInterface
     
         /**
          * @param RuleGroup $ruleGroup
    +     *
          * @return bool
          */
         public function moveRuleGroupDown(RuleGroup $ruleGroup)
    @@ -279,4 +285,128 @@ class RuleRepository implements RuleRepositoryInterface
         {
             return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get();
         }
    +
    +    /**
    +     * @param array $data
    +     *
    +     * @return Rule
    +     */
    +    public function storeRule(array $data)
    +    {
    +        /** @var RuleGroup $ruleGroup */
    +        $ruleGroup = Auth::user()->ruleGroups()->find($data['rule_group_id']);
    +
    +        // get max order:
    +        $order = $this->getHighestOrderInRuleGroup($ruleGroup);
    +
    +        // start by creating a new rule:
    +        $rule = new Rule;
    +        $rule->user()->associate(Auth::user());
    +
    +        $rule->rule_group_id   = $data['rule_group_id'];
    +        $rule->order           = ($order + 1);
    +        $rule->active          = 1;
    +        $rule->stop_processing = intval($data['stop_processing']) == 1;
    +        $rule->title           = $data['title'];
    +        $rule->description     = strlen($data['description']) > 0 ? $data['description'] : null;
    +
    +        $rule->save();
    +
    +        // start storing triggers:
    +        $order          = 1;
    +        $stopProcessing = false;
    +        $this->storeTrigger($rule, 'user_action', $data['trigger'], $stopProcessing, $order);
    +        foreach ($data['rule-triggers'] as $index => $trigger) {
    +            $value          = $data['rule-trigger-values'][$index];
    +            $stopProcessing = isset($data['rule-trigger-stop'][$index]) ? true : false;
    +            $this->storeTrigger($rule, $trigger, $value, $stopProcessing, $order);
    +            $order++;
    +        }
    +
    +        // same for actions.
    +        $order = 1;
    +        foreach ($data['rule-actions'] as $index => $action) {
    +            $value          = $data['rule-action-values'][$index];
    +            $stopProcessing = isset($data['rule-action-stop'][$index]) ? true : false;
    +            $this->storeAction($rule, $action, $value, $stopProcessing, $order);
    +        }
    +
    +        return $rule;
    +    }
    +
    +    /**
    +     * @param Rule   $rule
    +     * @param string $action
    +     * @param string $value
    +     * @param bool   $stopProcessing
    +     * @param int    $order
    +     *
    +     * @return RuleTrigger
    +     */
    +    public function storeTrigger(Rule $rule, $action, $value, $stopProcessing, $order)
    +    {
    +        $ruleTrigger = new RuleTrigger;
    +        $ruleTrigger->rule()->associate($rule);
    +        $ruleTrigger->order           = $order;
    +        $ruleTrigger->active          = 1;
    +        $ruleTrigger->stop_processing = $stopProcessing;
    +        $ruleTrigger->trigger_type    = $action;
    +        $ruleTrigger->trigger_value   = $value;
    +        $ruleTrigger->save();
    +
    +        return $ruleTrigger;
    +    }
    +
    +    /**
    +     * @param Rule $rule
    +     *
    +     * @return bool
    +     */
    +    public function destroyRule(Rule $rule)
    +    {
    +        foreach ($rule->ruleTriggers as $trigger) {
    +            $trigger->delete();
    +        }
    +        foreach ($rule->ruleActions as $action) {
    +            $action->delete();
    +        }
    +        $rule->delete();
    +
    +        return true;
    +    }
    +
    +
    +    /**
    +     * @param RuleGroup $ruleGroup
    +     *
    +     * @return int
    +     */
    +    public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup)
    +    {
    +        return intval($ruleGroup->rules()->max('order'));
    +    }
    +
    +    /**
    +     * @param Rule   $rule
    +     * @param string $action
    +     * @param string $value
    +     * @param bool   $stopProcessing
    +     * @param int    $order
    +     *
    +     * @return RuleAction
    +     */
    +    public function storeAction(Rule $rule, $action, $value, $stopProcessing, $order)
    +    {
    +        $ruleAction = new RuleAction;
    +        $ruleAction->rule()->associate($rule);
    +        $ruleAction->order           = $order;
    +        $ruleAction->active          = 1;
    +        $ruleAction->stop_processing = $stopProcessing;
    +        $ruleAction->action_type     = $action;
    +        $ruleAction->action_value    = $value;
    +        $ruleAction->save();
    +
    +
    +        return $ruleAction;
    +    }
     }
    \ No newline at end of file
    diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php
    index 429cf79911..7980a473e1 100644
    --- a/app/Repositories/Rule/RuleRepositoryInterface.php
    +++ b/app/Repositories/Rule/RuleRepositoryInterface.php
    @@ -10,7 +10,9 @@
     namespace FireflyIII\Repositories\Rule;
     
     use FireflyIII\Models\Rule;
    +use FireflyIII\Models\RuleAction;
     use FireflyIII\Models\RuleGroup;
    +use FireflyIII\Models\RuleTrigger;
     use Illuminate\Support\Collection;
     
     /**
    @@ -46,10 +48,17 @@ interface RuleRepositoryInterface
          * @param RuleGroup $ruleGroup
          * @param RuleGroup $moveTo
          *
    -     * @return boolean
    +     * @return bool
          */
         public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null);
     
    +    /**
    +     * @param Rule $rule
    +     *
    +     * @return bool
    +     */
    +    public function destroyRule(Rule $rule);
    +
         /**
          * @param Rule $rule
          * @param array $ids
    @@ -103,4 +112,41 @@ interface RuleRepositoryInterface
          */
         public function getRuleGroups();
     
    +    /**
    +     * @param array $data
    +     *
    +     * @return Rule
    +     */
    +    public function storeRule(array $data);
    +
    +    /**
    +     * @param RuleGroup $ruleGroup
    +     *
    +     * @return int
    +     */
    +    public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup);
    +
    +
    +    /**
    +     * @param Rule $rule
    +     * @param string $action
    +     * @param string $value
    +     * @param bool $stopProcessing
    +     * @param int $order
    +     *
    +     * @return RuleTrigger
    +     */
    +    public function storeTrigger(Rule $rule, $action, $value, $stopProcessing, $order);
    +
    +    /**
    +     * @param Rule $rule
    +     * @param string $action
    +     * @param string $value
    +     * @param bool $stopProcessing
    +     * @param int $order
    +     *
    +     * @return RuleAction
    +     */
    +    public function storeAction(Rule $rule, $action, $value, $stopProcessing, $order);
    +
     }
    \ No newline at end of file
    diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php
    index 1503518a1d..4d1b62321e 100644
    --- a/app/Validation/FireflyValidator.php
    +++ b/app/Validation/FireflyValidator.php
    @@ -8,7 +8,9 @@ use Crypt;
     use DB;
     use FireflyIII\Models\Account;
     use FireflyIII\Models\AccountType;
    +use FireflyIII\Models\Budget;
     use FireflyIII\Models\PiggyBank;
    +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
     use FireflyIII\User;
     use Illuminate\Contracts\Encryption\DecryptException;
     use Illuminate\Validation\Validator;
    @@ -39,7 +41,71 @@ class FireflyValidator extends Validator
          * @param $value
          * @param $parameters
          *
    -     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    +     * @return bool
    +     */
    +    public function validateRuleTriggerValue($attribute, $value, $parameters)
    +    {
    +        // loop all rule-triggers.
    +        // check if rule-value matches the thing.
    +        if (is_array($this->data['rule-trigger'])) {
    +            foreach ($this->data['rule-trigger'] as $index => $name) {
    +                $value = $this->data['rule-trigger-value'][$index] ?? false;
    +                switch ($name) {
    +                    default:
    +                        return true;
    +                    case 'amount_less':
    +                        return is_numeric($value);
    +                        break;
    +                    case 'transaction_type':
    +                        echo 'Implement me!';
    +                        exit;
    +                        break;
    +                }
    +            }
    +        }
    +    }
    +
    +    /**
    +     * @param $attribute
    +     * @param $value
    +     * @param $parameters
    +     *
    +     * @return bool
    +     */
    +    public function validateRuleActionValue($attribute, $value, $parameters)
    +    {
    +        // loop all rule-actions.
    +        // check if rule-action-value matches the thing.
    +        if (is_array($this->data['rule-action'])) {
    +            foreach ($this->data['rule-action'] as $index => $name) {
    +                $value = $this->data['rule-action-value'][$index] ?? false;
    +                switch ($name) {
    +                    default:
    +                        return true;
    +                    case 'set_budget':
    +                        /** @var BudgetRepositoryInterface $repository */
    +                        $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
    +                        $budgets    = $repository->getBudgets();
    +                        // count budgets, should have at least one
    +                        $count = $budgets->filter(
    +                            function (Budget $budget) use ($value) {
    +                                return $budget->name == $value;
    +                            }
    +                        )->count();
    +
    +                        return ($count === 1);
    +                }
    +            }
    +        }
    +
    +        return false;
    +    }
    +
    +
    +    /**
    +     * @param $attribute
    +     * @param $value
    +     * @param $parameters
          *
          * @return bool
          */
    diff --git a/public/js/rules/create-edit.js b/public/js/rules/create-edit.js
    index 6ba0fa2c2f..13c4027f10 100644
    --- a/public/js/rules/create-edit.js
    +++ b/public/js/rules/create-edit.js
    @@ -30,7 +30,7 @@ function addNewTrigger() {
     
     function addNewAction() {
         "use strict";
    -    triggerCount++;
    +    actionCount++;
     
         $.getJSON('json/action', {count: actionCount}).success(function (data) {
             //console.log(data.html);
    diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
    index b48de632fb..39cc843309 100644
    --- a/resources/lang/en_US/firefly.php
    +++ b/resources/lang/en_US/firefly.php
    @@ -62,6 +62,8 @@ return [
         'save_rules_by_moving'      => 'Save these rule(s) by moving them to another rule group:',
         'make_new_rule'             => 'Make new rule in rule group ":title"',
         'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.',
    +    'stored_new_rule'           => 'Stored new rule with title ":title"',
    +    'deleted_rule'              => 'Deleted rule with title ":title"',
     
         'trigger'                            => 'Trigger',
         'trigger_value'                      => 'Trigger on value',
    diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php
    index a8d6c60f32..ec273e435e 100644
    --- a/resources/lang/en_US/form.php
    +++ b/resources/lang/en_US/form.php
    @@ -78,12 +78,14 @@ return [
         'delete_currency'   => 'Delete currency ":name"',
         'delete_journal'    => 'Delete transaction with description ":description"',
         'delete_attachment' => 'Delete attachment ":name"',
    +    'delete_rule'       => 'Delete rule ":title"',
         'delete_rule_group' => 'Delete rule group ":title"',
     
         'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
         'account_areYouSure'    => 'Are you sure you want to delete the account named ":name"?',
         'bill_areYouSure'       => 'Are you sure you want to delete the bill named ":name"?',
    -    'ruleGroup_areYouSure'  => 'Are you sure you want to delete the rule group titles ":title"?',
    +    'rule_areYouSure'       => 'Are you sure you want to delete the rule titled ":title"?',
    +    'ruleGroup_areYouSure'  => 'Are you sure you want to delete the rule group titled ":title"?',
         'budget_areYouSure'     => 'Are you sure you want to delete the budget named ":name"?',
         'category_areYouSure'   => 'Are you sure you want to delete the category named ":name"?',
         'currency_areYouSure'   => 'Are you sure you want to delete the currency named ":name"?',
    diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php
    index 21dbc4ed0e..88e58d3a19 100644
    --- a/resources/lang/en_US/validation.php
    +++ b/resources/lang/en_US/validation.php
    @@ -1,6 +1,8 @@
      'This value is invalid for the selected trigger.',
    +    'rule_action_value'       => 'This value is invalid for the selected action.',
         'invalid_domain'          => 'Due to security constraints, you cannot register from this domain.',
         'file_already_attached'   => 'Uploaded file ":name" is already attached to this object.',
         'file_attached'           => 'Succesfully uploaded file ":name".',
    diff --git a/resources/views/rules/rule/delete.twig b/resources/views/rules/rule/delete.twig
    new file mode 100644
    index 0000000000..45571583a4
    --- /dev/null
    +++ b/resources/views/rules/rule/delete.twig
    @@ -0,0 +1,33 @@
    +
    +{% extends "./layout/default.twig" %}
    +
    +{% block breadcrumbs %}
    +    {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, rule) }}
    +{% endblock %}
    +
    +{% block content %}
    +    {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('rules.rule.destroy',rule.id) }) }}
    +    
    +
    +
    +
    +

    {{ trans('form.delete_rule', {'title': rule.title}) }}

    +
    +
    +

    + {{ trans('form.permDeleteWarning') }} +

    + +

    + {{ trans('form.rule_areYouSure', {'title': rule.title}) }} +

    +
    + +
    +
    +
    + {{ Form.close|raw }} +{% endblock %} From cd4cbdc197c5adb0990689be9ef42ca82d5258db Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 06:25:09 +0100 Subject: [PATCH 134/276] Accidentally used a php 7 thing. --- app/Validation/FireflyValidator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 4d1b62321e..72fe505804 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -49,7 +49,7 @@ class FireflyValidator extends Validator // check if rule-value matches the thing. if (is_array($this->data['rule-trigger'])) { foreach ($this->data['rule-trigger'] as $index => $name) { - $value = $this->data['rule-trigger-value'][$index] ?? false; + $value = isset($this->data['rule-trigger-value'][$index]) ? $this->data['rule-trigger-value'][$index] : false; switch ($name) { default: return true; @@ -78,7 +78,7 @@ class FireflyValidator extends Validator // check if rule-action-value matches the thing. if (is_array($this->data['rule-action'])) { foreach ($this->data['rule-action'] as $index => $name) { - $value = $this->data['rule-action-value'][$index] ?? false; + $value = isset($this->data['rule-action-value'][$index]) ? $this->data['rule-action-value'][$index] : false; switch ($name) { default: return true; From 5958990ed55e4db16f6f31a3744ce0bf0e1bb684 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 06:35:31 +0100 Subject: [PATCH 135/276] Stop processing. --- app/Rules/Processor.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index 9ef84fa551..45692c8603 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -87,6 +87,9 @@ class Processor if ($triggerClass->triggered()) { $hitTriggers++; } + if ($trigger->stop_processing) { + break; + } } Log::debug('Total: ' . $foundTriggers . ' found triggers. ' . $hitTriggers . ' triggers were hit.'); @@ -114,6 +117,9 @@ class Processor /** @var ActionInterface $actionClass */ $actionClass = new $class($action, $this->journal); $actionClass->act(); + if ($action->stop_processing) { + break; + } } From af1c6b22bb7c65ffafa132b150e7bd579feaeca1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 06:35:53 +0100 Subject: [PATCH 136/276] Translation. --- resources/lang/en_US/firefly.php | 1 + resources/views/rules/rule/create.twig | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 39cc843309..87d37a9c68 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -64,6 +64,7 @@ return [ 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', 'stored_new_rule' => 'Stored new rule with title ":title"', 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', 'trigger' => 'Trigger', 'trigger_value' => 'Trigger on value', diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index 8c907753f4..c3f15ce345 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -107,10 +107,10 @@

    {{ 'options'|_ }}

    - {{ ExpandedForm.optionsList('create','rule-group') }} + {{ ExpandedForm.optionsList('create','rule') }}
    From 4cc6d57e6eb350e32383bec0337be35d726cd47b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 06:43:00 +0100 Subject: [PATCH 137/276] Stop processing other rules if asked. --- app/Handlers/Events/FireRulesForStore.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 37506ca9dd..daa0a2993f 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -67,6 +67,10 @@ class FireRulesForStore // get some return out of this? $processor->handle(); + if($rule->stop_processing) { + break; + } + } } // echo 'Done processing rules. See log.'; From 8b66ff6afec5a672fd1b645339130990b0b86da6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 08:04:57 +0100 Subject: [PATCH 138/276] First edit-rule code. --- app/Http/Controllers/RuleController.php | 88 +++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 92f1a8e152..26caacfc53 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -235,6 +235,94 @@ class RuleController extends Controller return redirect(Session::get('rules.rule-group.create.url')); } + public function editRule(Rule $rule) { + + // count for current rule's triggers/actions. + $triggerCount = 0; + $actionCount = 0; + + // collection of those triggers/actions. + $triggers = []; + $actions = []; + + // array of valid values for triggers (for form). + $ruleTriggers = array_keys(Config::get('firefly.rule-triggers')); + $possibleTriggers = []; + foreach ($ruleTriggers as $key) { + if ($key != 'user_action') { + $possibleTriggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice'); + } + } + unset($key, $ruleTriggers); + + // array of valid values for actions + $ruleActions = array_keys(Config::get('firefly.rule-actions')); + $possibleActions = []; + foreach ($ruleActions as $key) { + $possibleActions[$key] = trans('firefly.rule_action_' . $key . '_choice'); + } + unset($key, $ruleActions); + + + // has old input? + if (Input::old()) { + // process old triggers. + $newIndex = 0; + foreach (Input::old('rule-trigger') as $index => $entry) { + $count = ($newIndex + 1); + $triggerCount++; + $oldTrigger = $entry; + $oldValue = Input::old('rule-trigger-value')[$index]; + $oldChecked = isset(Input::old('rule-trigger-stop')[$index]) ? true : false; + $oldTriggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => $oldTrigger, + 'oldValue' => $oldValue, + 'oldChecked' => $oldChecked, + 'triggers' => $possibleTriggers, + 'count' => $count + ] + )->render(); + $newIndex++; + } + + // process old actions + $newIndex = 0; + foreach (Input::old('rule-action') as $index => $entry) { + $count = ($newIndex + 1); + $actionCount++; + $oldAction = $entry; + $oldValue = Input::old('rule-action-value')[$index]; + $oldChecked = isset(Input::old('rule-action-stop')[$index]) ? true : false; + $oldActions[] = view( + 'rules.partials.action', + [ + 'oldTrigger' => $oldAction, + 'oldValue' => $oldValue, + 'oldChecked' => $oldChecked, + 'actions' => $possibleActions, + 'count' => $count + ] + )->render(); + $newIndex++; + } + } + + + $subTitle = trans('firefly.edit_rule_group', ['title' => $rule->title]); + + // put previous url in session if not redirect from store (not "return_to_edit"). + if (Session::get('rules.rule.edit.fromUpdate') !== true) { + Session::put('rules.rule.edit.url', URL::previous()); + } + Session::forget('rules.rule.edit.fromUpdate'); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'edit-rule'); + + return view('rules.rule.edit', compact('rule', 'subTitle')); + } + /** * @param RuleGroup $ruleGroup From 006c2ae18648c3336fa0bc82940c4b32af64f2d8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 08:06:33 +0100 Subject: [PATCH 139/276] Refer to dedicated methods instead of variables. --- resources/views/rules/partials/action.twig | 2 +- resources/views/rules/partials/trigger.twig | 2 +- resources/views/rules/rule/create.twig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/rules/partials/action.twig b/resources/views/rules/partials/action.twig index e7ec792c79..1e5d1247bc 100644 --- a/resources/views/rules/partials/action.twig +++ b/resources/views/rules/partials/action.twig @@ -9,7 +9,7 @@ {% endif %}
    @@ -15,6 +16,7 @@
    {{ ExpandedForm.text('title') }} {{ ExpandedForm.select('trigger',allJournalTriggers(), primaryTrigger) }} + {{ ExpandedForm.checkbox('active',1,rule.active, {helpText: trans('firefly.rule_help_active')}) }} {{ ExpandedForm.checkbox('stop_processing',1,rule.stop_processing, {helpText: trans('firefly.rule_help_stop_processing')}) }}
    From fcf6cdb134f0809bef0f5c8a96dac161053a39fa Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 10:52:35 +0100 Subject: [PATCH 144/276] Better order and display. --- app/Http/Controllers/RuleController.php | 2 ++ resources/views/rules/index.twig | 36 ++++++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index fe70fd6937..9d6337e64e 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -548,10 +548,12 @@ class RuleController extends Controller { $ruleGroups = Auth::user() ->ruleGroups() + ->orderBy('active', 'DESC') ->orderBy('order', 'ASC') ->with( [ 'rules' => function ($query) { + $query->orderBy('active', 'DESC'); $query->orderBy('order', 'ASC'); }, diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 238a4a542b..546e74043b 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -28,7 +28,13 @@
    -

    {{ ruleGroup.title }}

    +

    + {% if ruleGroup.active %} + {{ ruleGroup.title }} + {% else %} + {{ ruleGroup.title }} (inactive) + {% endif %} +

    @@ -104,13 +110,22 @@
    - {{ rule.title }} + + {% if rule.active %} + {{ rule.title }} + {% else %} + {{ rule.title }} (inactive) + {% endif %} {% if rule.stop_processing %} {% endif %} {% if rule.description != "" %} -
    {{ rule.description }}
    +
    {{ rule.description }}
    {% endif %} @@ -119,7 +134,11 @@
      {% for trigger in rule.ruleTriggers %} {% if trigger.trigger_type != "user_action" %} -
    • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }} +
    • {{ trans(('firefly.rule_trigger_' ~ trigger.trigger_type), {trigger_value: trigger.trigger_value}) }} {% if trigger.stop_processing %} @@ -134,7 +153,11 @@ {% if rule.ruleActions.count > 0 %}
        {% for action in rule.ruleActions %} -
      • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }} +
      • {{ trans(('firefly.rule_action_' ~ action.action_type), {action_value: action.action_value}) }} {% if action.stop_processing %} {% endif %} @@ -154,7 +177,8 @@ {% endif %}


        - {{ 'new_rule'|_ }} + {{ 'new_rule'|_ }}

    From 2605f60983426a369cc54acff66ea8d8f8f990b6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 11:16:41 +0100 Subject: [PATCH 145/276] Refactor. Split rules and rule groups. --- app/Repositories/RuleGroup/RuleGroupRepository.php | 9 +++++++++ .../RuleGroup/RuleGroupRepositoryInterface.php | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 app/Repositories/RuleGroup/RuleGroupRepository.php create mode 100644 app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php new file mode 100644 index 0000000000..b8bdc5bc71 --- /dev/null +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -0,0 +1,9 @@ + Date: Fri, 15 Jan 2016 11:27:27 +0100 Subject: [PATCH 146/276] Split rule and rule group repositories. --- app/Repositories/Rule/RuleRepository.php | 199 +++--------------- .../Rule/RuleRepositoryInterface.php | 68 +----- .../RuleGroup/RuleGroupRepository.php | 188 ++++++++++++++++- .../RuleGroupRepositoryInterface.php | 63 +++++- 4 files changed, 282 insertions(+), 236 deletions(-) diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 09659c2c30..1521372f3d 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -23,59 +23,6 @@ use Illuminate\Support\Collection; */ class RuleRepository implements RuleRepositoryInterface { - /** - * @return int - */ - public function getHighestOrderRuleGroup() - { - $entry = Auth::user()->ruleGroups()->max('order'); - - return intval($entry); - } - - /** - * @param array $data - * - * @return RuleGroup - */ - public function storeRuleGroup(array $data) - { - $order = $this->getHighestOrderRuleGroup(); - - $newRuleGroup = new RuleGroup( - [ - 'user_id' => $data['user'], - 'title' => $data['title'], - 'description' => $data['description'], - 'order' => ($order + 1), - 'active' => 1, - - - ] - ); - $newRuleGroup->save(); - $this->resetRuleGroupOrder(); - - return $newRuleGroup; - } - - /** - * @param RuleGroup $ruleGroup - * @param array $data - * - * @return RuleGroup - */ - public function updateRuleGroup(RuleGroup $ruleGroup, array $data) - { - // update the account: - $ruleGroup->title = $data['title']; - $ruleGroup->description = $data['description']; - $ruleGroup->active = $data['active']; - $ruleGroup->save(); - $this->resetRuleGroupOrder(); - - return $ruleGroup; - } /** * @param Rule $rule @@ -99,6 +46,30 @@ class RuleRepository implements RuleRepositoryInterface return true; } + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function resetRulesInGroupOrder(RuleGroup $ruleGroup) + { + $ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]); + + $set = $ruleGroup->rules() + ->orderBy('order', 'ASC') + ->orderBy('updated_at', 'DESC') + ->get(); + $count = 1; + /** @var Rule $entry */ + foreach ($set as $entry) { + $entry->order = $count; + $entry->save(); + $count++; + } + + return true; + + } + /** * @param Rule $rule * @param array $ids @@ -121,56 +92,6 @@ class RuleRepository implements RuleRepositoryInterface return true; } - /** - * @param RuleGroup $ruleGroup - * @param RuleGroup $moveTo - * - * @return boolean - */ - public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null) - { - /** @var Rule $rule */ - foreach ($ruleGroup->rules as $rule) { - - if (is_null($moveTo)) { - - $rule->delete(); - } else { - // move - $rule->ruleGroup()->associate($moveTo); - $rule->save(); - } - } - - $ruleGroup->delete(); - - $this->resetRuleGroupOrder(); - if (!is_null($moveTo)) { - $this->resetRulesInGroupOrder($moveTo); - } - - return true; - } - - /** - * @return bool - */ - public function resetRuleGroupOrder() - { - Auth::user()->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]); - - $set = Auth::user()->ruleGroups()->where('active', 1)->orderBy('order', 'ASC')->get(); - $count = 1; - /** @var RuleGroup $entry */ - foreach ($set as $entry) { - $entry->order = $count; - $entry->save(); - $count++; - } - - - return true; - } /** * @param Rule $rule @@ -215,76 +136,6 @@ class RuleRepository implements RuleRepositoryInterface $this->resetRulesInGroupOrder($rule->ruleGroup); } - /** - * @return bool - */ - public function resetRulesInGroupOrder(RuleGroup $ruleGroup) - { - $ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]); - - $set = $ruleGroup->rules() - ->orderBy('order', 'ASC') - ->orderBy('updated_at', 'DESC') - ->get(); - $count = 1; - /** @var Rule $entry */ - foreach ($set as $entry) { - $entry->order = $count; - $entry->save(); - $count++; - } - - } - - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ - public function moveRuleGroupUp(RuleGroup $ruleGroup) - { - $order = $ruleGroup->order; - - // find the rule with order-1 and give it order+1 - $other = Auth::user()->ruleGroups()->where('order', ($order - 1))->first(); - if ($other) { - $other->order = ($other->order + 1); - $other->save(); - } - - $ruleGroup->order = ($ruleGroup->order - 1); - $ruleGroup->save(); - $this->resetRuleGroupOrder(); - } - - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ - public function moveRuleGroupDown(RuleGroup $ruleGroup) - { - $order = $ruleGroup->order; - - // find the rule with order+1 and give it order-1 - $other = Auth::user()->ruleGroups()->where('order', ($order + 1))->first(); - if ($other) { - $other->order = ($other->order - 1); - $other->save(); - } - - $ruleGroup->order = ($ruleGroup->order + 1); - $ruleGroup->save(); - $this->resetRuleGroupOrder(); - } - - /** - * @return Collection - */ - public function getRuleGroups() - { - return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get(); - } /** * @param array $data @@ -375,7 +226,6 @@ class RuleRepository implements RuleRepositoryInterface return true; } - /** * @param RuleGroup $ruleGroup * @@ -386,6 +236,7 @@ class RuleRepository implements RuleRepositoryInterface return intval($ruleGroup->rules()->max('order')); } + /** * @param Rule $rule * @param string $action diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index cab6741a28..80a3e832b9 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -22,36 +22,6 @@ use Illuminate\Support\Collection; */ interface RuleRepositoryInterface { - /** - * @param array $data - * - * @return RuleGroup - */ - public function storeRuleGroup(array $data); - - /** - * @return int - */ - public function getHighestOrderRuleGroup(); - - - /** - * @param RuleGroup $ruleGroup - * @param array $data - * - * @return RuleGroup - */ - public function updateRuleGroup(RuleGroup $ruleGroup, array $data); - - - /** - * @param RuleGroup $ruleGroup - * @param RuleGroup $moveTo - * - * @return bool - */ - public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null); - /** * @param Rule $rule * @@ -59,6 +29,13 @@ interface RuleRepositoryInterface */ public function destroyRule(Rule $rule); + /** + * @param RuleGroup $ruleGroup + * + * @return int + */ + public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup); + /** * @param Rule $rule * @param array $ids @@ -74,11 +51,7 @@ interface RuleRepositoryInterface public function reorderRuleActions(Rule $rule, array $ids); /** - * @return bool - */ - public function resetRuleGroupOrder(); - - /** + * @param RuleGroup $ruleGroup * @return bool */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup); @@ -102,23 +75,6 @@ interface RuleRepositoryInterface */ public function moveRuleDown(Rule $rule); - /** - * @param RuleGroup $ruleGroup - * @return bool - */ - public function moveRuleGroupUp(RuleGroup $ruleGroup); - - /** - * @param RuleGroup $ruleGroup - * @return bool - */ - public function moveRuleGroupDown(RuleGroup $ruleGroup); - - /** - * @return Collection - */ - public function getRuleGroups(); - /** * @param array $data * @@ -126,14 +82,6 @@ interface RuleRepositoryInterface */ public function storeRule(array $data); - /** - * @param RuleGroup $ruleGroup - * - * @return int - */ - public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup); - - /** * @param Rule $rule * @param string $action diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index b8bdc5bc71..9c5b3167b5 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -3,7 +3,193 @@ namespace FireflyIII\Repositories\RuleGroup; -class RuleGroupRepository +use Auth; +use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleGroup; +use Illuminate\Support\Collection; + +class RuleGroupRepository implements RuleGroupRepositoryInterface { + /** + * @param RuleGroup $ruleGroup + * @param RuleGroup $moveTo + * + * @return boolean + */ + public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null) + { + /** @var Rule $rule */ + foreach ($ruleGroup->rules as $rule) { + + if (is_null($moveTo)) { + + $rule->delete(); + } else { + // move + $rule->ruleGroup()->associate($moveTo); + $rule->save(); + } + } + + $ruleGroup->delete(); + + $this->resetRuleGroupOrder(); + if (!is_null($moveTo)) { + $this->resetRulesInGroupOrder($moveTo); + } + + return true; + } + + + /** + * @return int + */ + public function getHighestOrderRuleGroup() + { + $entry = Auth::user()->ruleGroups()->max('order'); + + return intval($entry); + } + + /** + * @return Collection + */ + public function getRuleGroups() + { + return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get(); + } + + + /** + * @param RuleGroup $ruleGroup + * + * @return bool + */ + public function moveRuleGroupUp(RuleGroup $ruleGroup) + { + $order = $ruleGroup->order; + + // find the rule with order-1 and give it order+1 + $other = Auth::user()->ruleGroups()->where('order', ($order - 1))->first(); + if ($other) { + $other->order = ($other->order + 1); + $other->save(); + } + + $ruleGroup->order = ($ruleGroup->order - 1); + $ruleGroup->save(); + $this->resetRuleGroupOrder(); + } + + /** + * @param RuleGroup $ruleGroup + * + * @return bool + */ + public function moveRuleGroupDown(RuleGroup $ruleGroup) + { + $order = $ruleGroup->order; + + // find the rule with order+1 and give it order-1 + $other = Auth::user()->ruleGroups()->where('order', ($order + 1))->first(); + if ($other) { + $other->order = ($other->order - 1); + $other->save(); + } + + $ruleGroup->order = ($ruleGroup->order + 1); + $ruleGroup->save(); + $this->resetRuleGroupOrder(); + } + + + /** + * @return bool + */ + public function resetRuleGroupOrder() + { + Auth::user()->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]); + + $set = Auth::user()->ruleGroups()->where('active', 1)->orderBy('order', 'ASC')->get(); + $count = 1; + /** @var RuleGroup $entry */ + foreach ($set as $entry) { + $entry->order = $count; + $entry->save(); + $count++; + } + + + return true; + } + + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function resetRulesInGroupOrder(RuleGroup $ruleGroup) + { + $ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]); + + $set = $ruleGroup->rules() + ->orderBy('order', 'ASC') + ->orderBy('updated_at', 'DESC') + ->get(); + $count = 1; + /** @var Rule $entry */ + foreach ($set as $entry) { + $entry->order = $count; + $entry->save(); + $count++; + } + + return true; + + } + + /** + * @param array $data + * + * @return RuleGroup + */ + public function storeRuleGroup(array $data) + { + $order = $this->getHighestOrderRuleGroup(); + + $newRuleGroup = new RuleGroup( + [ + 'user_id' => $data['user'], + 'title' => $data['title'], + 'description' => $data['description'], + 'order' => ($order + 1), + 'active' => 1, + + + ] + ); + $newRuleGroup->save(); + $this->resetRuleGroupOrder(); + + return $newRuleGroup; + } + + /** + * @param RuleGroup $ruleGroup + * @param array $data + * + * @return RuleGroup + */ + public function updateRuleGroup(RuleGroup $ruleGroup, array $data) + { + // update the account: + $ruleGroup->title = $data['title']; + $ruleGroup->description = $data['description']; + $ruleGroup->active = $data['active']; + $ruleGroup->save(); + $this->resetRuleGroupOrder(); + + return $ruleGroup; + } } \ No newline at end of file diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index d69658cc9b..213ab70766 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -3,7 +3,68 @@ namespace FireflyIII\Repositories\RuleGroup; -class RuleGroupRepositoryInterface +use FireflyIII\Models\RuleGroup; +use Illuminate\Support\Collection; + +interface RuleGroupRepositoryInterface { + /** + * @param RuleGroup $ruleGroup + * @param RuleGroup $moveTo + * + * @return bool + */ + public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null); + + + + /** + * @return int + */ + public function getHighestOrderRuleGroup(); + + /** + * @return Collection + */ + public function getRuleGroups(); + + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function moveRuleGroupUp(RuleGroup $ruleGroup); + + /** + * @param RuleGroup $ruleGroup + * @return bool + */ + public function moveRuleGroupDown(RuleGroup $ruleGroup); + + /** + * @return bool + */ + public function resetRuleGroupOrder(); + + /** + * @return bool + */ + public function resetRulesInGroupOrder(RuleGroup $ruleGroup); + + /** + * @param array $data + * + * @return RuleGroup + */ + public function storeRuleGroup(array $data); + + + /** + * @param RuleGroup $ruleGroup + * @param array $data + * + * @return RuleGroup + */ + public function updateRuleGroup(RuleGroup $ruleGroup, array $data); + } \ No newline at end of file From 466d739da811e6b2877016236141fec3ef6dba28 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 11:28:27 +0100 Subject: [PATCH 147/276] Start with new controller for rule groups. --- app/Http/Controllers/RuleGroupController.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/Http/Controllers/RuleGroupController.php diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php new file mode 100644 index 0000000000..8c08ddaddf --- /dev/null +++ b/app/Http/Controllers/RuleGroupController.php @@ -0,0 +1,19 @@ + Date: Fri, 15 Jan 2016 13:06:17 +0100 Subject: [PATCH 148/276] Move methods around --- app/Http/Controllers/RuleController.php | 170 +---------------- app/Http/Controllers/RuleGroupController.php | 190 +++++++++++++++++++ app/Http/routes.php | 27 ++- 3 files changed, 205 insertions(+), 182 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 9d6337e64e..b9de5fc270 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -44,25 +44,6 @@ class RuleController extends Controller View::share('mainTitleIcon', 'fa-random'); } - /** - * @return View - */ - public function createRuleGroup() - { - $subTitleIcon = 'fa-clone'; - $subTitle = trans('firefly.make_new_rule_group'); - - // put previous url in session if not redirect from store (not "create another"). - if (Session::get('rules.rule-group.create.fromStore') !== true) { - Session::put('rules.rule-group.create.url', URL::previous()); - } - Session::forget('rules.rule-group.create.fromStore'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'create-rule-group'); - - return view('rules.rule-group.create', compact('subTitleIcon', 'what', 'subTitle')); - } - /** * @param RuleFormRequest $request * @param RuleRepositoryInterface $repository @@ -180,35 +161,10 @@ class RuleController extends Controller } /** - * @param RuleGroupFormRequest $request - * @param RuleRepositoryInterface $repository + * @param Rule $rule * - * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @return View */ - public function storeRuleGroup(RuleGroupFormRequest $request, RuleRepositoryInterface $repository) - { - $data = [ - 'title' => $request->input('title'), - 'description' => $request->input('description'), - 'user' => Auth::user()->id, - ]; - - $ruleGroup = $repository->storeRuleGroup($data); - - Session::flash('success', trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])); - Preferences::mark(); - - if (intval(Input::get('create_another')) === 1) { - // set value so create routine will not overwrite URL: - Session::put('rules.rule-group.create.fromStore', true); - - return redirect(route('rules.rule-group.create'))->withInput(); - } - - // redirect to previous URL. - return redirect(Session::get('rules.rule-group.create.url')); - } - public function editRule(Rule $rule) { @@ -371,60 +327,6 @@ class RuleController extends Controller return redirect(Session::get('rules.rule.edit.url')); } - - /** - * @param RuleGroup $ruleGroup - * - * @return View - */ - public function editRuleGroup(RuleGroup $ruleGroup) - { - $subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]); - - // put previous url in session if not redirect from store (not "return_to_edit"). - if (Session::get('rules.rule-group.edit.fromUpdate') !== true) { - Session::put('rules.rule-group.edit.url', URL::previous()); - } - Session::forget('rules.rule-group.edit.fromUpdate'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'edit-rule-group'); - - return view('rules.rule-group.edit', compact('ruleGroup', 'subTitle')); - - } - - /** - * @param RuleGroupFormRequest $request - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup - * - * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function updateRuleGroup(RuleGroupFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup) - { - $data = [ - 'title' => $request->input('title'), - 'description' => $request->input('description'), - 'active' => intval($request->input('active')) == 1, - ]; - - $repository->updateRuleGroup($ruleGroup, $data); - - Session::flash('success', trans('firefly.updated_rule_group', ['title' => $ruleGroup->title])); - Preferences::mark(); - - if (intval(Input::get('return_to_edit')) === 1) { - // set value so edit routine will not overwrite URL: - Session::put('rules.rule-group.edit.fromUpdate', true); - - return redirect(route('rules.rule-group.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]); - } - - // redirect to previous URL. - return redirect(Session::get('rules.rule-group.edit.url')); - - } - /** * @param RuleRepositoryInterface $repository * @param Rule $rule @@ -444,26 +346,7 @@ class RuleController extends Controller } - /** - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup - * - * @return View - */ - public function deleteRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) - { - $subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]); - $ruleGroupList = Expandedform::makeSelectList($repository->getRuleGroups(), true); - unset($ruleGroupList[$ruleGroup->id]); - - // put previous url in session - Session::put('rules.rule-group.delete.url', URL::previous()); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'delete-rule-group'); - - return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle', 'ruleGroupList')); - } /** * @param Rule $rule @@ -484,28 +367,6 @@ class RuleController extends Controller return redirect(Session::get('rules.rule.delete.url')); } - /** - * @param RuleGroup $ruleGroup - * @param RuleRepositoryInterface $repository - * - * @return \Illuminate\Http\RedirectResponse - */ - public function destroyRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) - { - - $title = $ruleGroup->title; - $moveTo = Auth::user()->ruleGroups()->find(intval(Input::get('move_rules_before_delete'))); - - $repository->destroyRuleGroup($ruleGroup, $moveTo); - - - Session::flash('success', trans('firefly.deleted_rule_group', ['title' => $title])); - Preferences::mark(); - - - return redirect(Session::get('rules.rule-group.delete.url')); - } - /** * @param RuleRepositoryInterface $repository * @param Rule $rule @@ -598,32 +459,5 @@ class RuleController extends Controller } - /** - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function upRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) - { - $repository->moveRuleGroupUp($ruleGroup); - - return redirect(route('rules.index')); - - } - - /** - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function downRuleGroup(RuleRepositoryInterface $repository, RuleGroup $ruleGroup) - { - $repository->moveRuleGroupDown($ruleGroup); - - return redirect(route('rules.index')); - - } } diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 8c08ddaddf..f11eab8e29 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -2,13 +2,28 @@ namespace FireflyIII\Http\Controllers; +use Auth; +use ExpandedForm; +use FireflyIII\Http\Requests\RuleGroupFormRequest; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use Input; +use Preferences; +use Session; +use URL; use View; + /** * Class RuleGroupController + * * @package FireflyIII\Http\Controllers */ class RuleGroupController extends Controller { + /** + * RuleGroupController constructor. + */ public function __construct() { parent::__construct(); @@ -16,4 +31,179 @@ class RuleGroupController extends Controller View::share('mainTitleIcon', 'fa-random'); } + /** + * @return View + */ + public function createRuleGroup() + { + $subTitleIcon = 'fa-clone'; + $subTitle = trans('firefly.make_new_rule_group'); + + // put previous url in session if not redirect from store (not "create another"). + if (Session::get('rules.rule-group.create.fromStore') !== true) { + Session::put('rules.rule-group.create.url', URL::previous()); + } + Session::forget('rules.rule-group.create.fromStore'); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'create-rule-group'); + + return view('rules.rule-group.create', compact('subTitleIcon', 'what', 'subTitle')); + } + + + /** + * @param RuleGroupFormRequest $request + * @param RuleRepositoryInterface $repository + * + * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function storeRuleGroup(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository) + { + $data = [ + 'title' => $request->input('title'), + 'description' => $request->input('description'), + 'user' => Auth::user()->id, + ]; + + $ruleGroup = $repository->storeRuleGroup($data); + + Session::flash('success', trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])); + Preferences::mark(); + + if (intval(Input::get('create_another')) === 1) { + // set value so create routine will not overwrite URL: + Session::put('rules.rule-group.create.fromStore', true); + + return redirect(route('rules.rule-group.create'))->withInput(); + } + + // redirect to previous URL. + return redirect(Session::get('rules.rule-group.create.url')); + } + + /** + * @param RuleGroup $ruleGroup + * + * @return View + */ + public function editRuleGroup(RuleGroup $ruleGroup) + { + $subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]); + + // put previous url in session if not redirect from store (not "return_to_edit"). + if (Session::get('rules.rule-group.edit.fromUpdate') !== true) { + Session::put('rules.rule-group.edit.url', URL::previous()); + } + Session::forget('rules.rule-group.edit.fromUpdate'); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'edit-rule-group'); + + return view('rules.rule-group.edit', compact('ruleGroup', 'subTitle')); + + } + + /** + * @param RuleGroupFormRequest $request + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * + * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function updateRuleGroup(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $data = [ + 'title' => $request->input('title'), + 'description' => $request->input('description'), + 'active' => intval($request->input('active')) == 1, + ]; + + $repository->updateRuleGroup($ruleGroup, $data); + + Session::flash('success', trans('firefly.updated_rule_group', ['title' => $ruleGroup->title])); + Preferences::mark(); + + if (intval(Input::get('return_to_edit')) === 1) { + // set value so edit routine will not overwrite URL: + Session::put('rules.rule-group.edit.fromUpdate', true); + + return redirect(route('rules.rule-group.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]); + } + + // redirect to previous URL. + return redirect(Session::get('rules.rule-group.edit.url')); + + } + + /** + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * + * @return View + */ + public function deleteRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]); + + $ruleGroupList = Expandedform::makeSelectList($repository->getRuleGroups(), true); + unset($ruleGroupList[$ruleGroup->id]); + + // put previous url in session + Session::put('rules.rule-group.delete.url', URL::previous()); + Session::flash('gaEventCategory', 'rules'); + Session::flash('gaEventAction', 'delete-rule-group'); + + return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle', 'ruleGroupList')); + } + + /** + * @param RuleGroup $ruleGroup + * @param RuleRepositoryInterface $repository + * + * @return \Illuminate\Http\RedirectResponse + */ + public function destroyRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + { + + $title = $ruleGroup->title; + $moveTo = Auth::user()->ruleGroups()->find(intval(Input::get('move_rules_before_delete'))); + + $repository->destroyRuleGroup($ruleGroup, $moveTo); + + + Session::flash('success', trans('firefly.deleted_rule_group', ['title' => $title])); + Preferences::mark(); + + + return redirect(Session::get('rules.rule-group.delete.url')); + } + + + /** + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function upRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $repository->moveRuleGroupUp($ruleGroup); + + return redirect(route('rules.index')); + + } + + /** + * @param RuleRepositoryInterface $repository + * @param RuleGroup $ruleGroup + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function downRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + { + $repository->moveRuleGroupDown($ruleGroup); + + return redirect(route('rules.index')); + + } + } \ No newline at end of file diff --git a/app/Http/routes.php b/app/Http/routes.php index 5c9041ebbc..14a6424893 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -236,36 +236,35 @@ Route::group( /** * Rules Controller */ + // index Route::get('/rules', ['uses' => 'RuleController@index', 'as' => 'rules.index']); - // rules: + // rules GET: Route::get('/rules/create/{ruleGroup}', ['uses' => 'RuleController@createRule', 'as' => 'rules.rule.create']); - Route::get('/rules/rules/up/{rule}', ['uses' => 'RuleController@upRule', 'as' => 'rules.rule.up']); Route::get('/rules/rules/down/{rule}', ['uses' => 'RuleController@downRule', 'as' => 'rules.rule.down']); Route::get('/rules/rules/edit/{rule}', ['uses' => 'RuleController@editRule', 'as' => 'rules.rule.edit']); Route::get('/rules/rules/delete/{rule}', ['uses' => 'RuleController@deleteRule', 'as' => 'rules.rule.delete']); + // rules POST: Route::post('/rules/rules/trigger/reorder/{rule}', ['uses' => 'RuleController@reorderRuleTriggers']); Route::post('/rules/rules/action/reorder/{rule}', ['uses' => 'RuleController@reorderRuleActions']); - Route::post('/rules/store/{ruleGroup}', ['uses' => 'RuleController@storeRule', 'as' => 'rules.rule.store']); Route::post('/rules/update/{rule}', ['uses' => 'RuleController@updateRule', 'as' => 'rules.rule.update']); Route::post('/rules/destroy/{rule}', ['uses' => 'RuleController@destroyRule', 'as' => 'rules.rule.destroy']); - // rule groups: - Route::get('/rules/groups/create', ['uses' => 'RuleController@createRuleGroup', 'as' => 'rules.rule-group.create']); - Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleController@editRuleGroup', 'as' => 'rules.rule-group.edit']); - Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); + // rule groups GET + Route::get('/rules/groups/create', ['uses' => 'RuleGroupController@createRuleGroup', 'as' => 'rules.rule-group.create']); + Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleGroupController@editRuleGroup', 'as' => 'rules.rule-group.edit']); + Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleGroupController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); + Route::get('/rules/groups/up/{ruleGroup}', ['uses' => 'RuleGroupController@upRuleGroup', 'as' => 'rules.rule-group.up']); + Route::get('/rules/groups/down/{ruleGroup}', ['uses' => 'RuleGroupController@downRuleGroup', 'as' => 'rules.rule-group.down']); - Route::get('/rules/groups/up/{ruleGroup}', ['uses' => 'RuleController@upRuleGroup', 'as' => 'rules.rule-group.up']); - Route::get('/rules/groups/down/{ruleGroup}', ['uses' => 'RuleController@downRuleGroup', 'as' => 'rules.rule-group.down']); - - - Route::post('/rules/groups/store', ['uses' => 'RuleController@storeRuleGroup', 'as' => 'rules.rule-group.store']); - Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleController@updateRuleGroup', 'as' => 'rules.rule-group.update']); - Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleController@destroyRuleGroup', 'as' => 'rules.rule-group.destroy']); + // rule groups POST + Route::post('/rules/groups/store', ['uses' => 'RuleGroupController@storeRuleGroup', 'as' => 'rules.rule-group.store']); + Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleGroupController@updateRuleGroup', 'as' => 'rules.rule-group.update']); + Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleGroupController@destroyRuleGroup', 'as' => 'rules.rule-group.destroy']); /** * Search Controller From 41f200e630d134092d2e5659e704158e308e5065 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 13:08:25 +0100 Subject: [PATCH 149/276] Some code cleanup. --- .../Controllers/Chart/CategoryController.php | 6 ++-- app/Http/Controllers/PiggyBankController.php | 2 +- app/Http/Controllers/RuleController.php | 18 ++++++------ app/Http/Controllers/RuleGroupController.php | 28 +++++++++---------- app/Http/Middleware/Binder.php | 5 ++++ 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 2314d61c74..028174d80e 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -121,12 +121,12 @@ class CategoryController extends Controller $outside = $repository->sumSpentNoCategory(new Collection, $start, $end); // this is a "fake" entry for the "no category" entry. - $entry = new stdClass(); - $entry->name = trans('firefly.no_category'); + $entry = new stdClass(); + $entry->name = trans('firefly.no_category'); $entry->spent = $outside; $set->push($entry); - $set = $set->sortBy('spent'); + $set = $set->sortBy('spent'); $data = $this->generator->frontpage($set); $cache->store($data); diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 92ceb4cb88..83ed844119 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -156,7 +156,7 @@ class PiggyBankController extends Controller } /** - * @param ARI $repository + * @param ARI $repository * @param PiggyBankRepositoryInterface $piggyRepository * * @return View diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index b9de5fc270..05cf1be3e3 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -10,11 +10,8 @@ namespace FireflyIII\Http\Controllers; use Auth; -use Config; -use ExpandedForm; use FireflyIII\Http\Requests; use FireflyIII\Http\Requests\RuleFormRequest; -use FireflyIII\Http\Requests\RuleGroupFormRequest; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; @@ -284,14 +281,19 @@ class RuleController extends Controller Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'edit-rule'); - return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', - 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount')); + return view( + 'rules.rule.edit', compact( + 'rule', 'subTitle', 'primaryTrigger', + 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount' + ) + ); } /** * @param RuleRepositoryInterface $repository * @param RuleFormRequest $request * @param Rule $rule + * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function updateRule(RuleRepositoryInterface $repository, RuleFormRequest $request, Rule $rule) @@ -328,10 +330,10 @@ class RuleController extends Controller } /** - * @param RuleRepositoryInterface $repository - * @param Rule $rule + * @param Rule $rule * * @return View + * @internal param RuleRepositoryInterface $repository */ public function deleteRule(Rule $rule) { @@ -346,8 +348,6 @@ class RuleController extends Controller } - - /** * @param Rule $rule * @param RuleRepositoryInterface $repository diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index f11eab8e29..1444da86a8 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -6,7 +6,6 @@ use Auth; use ExpandedForm; use FireflyIII\Http\Requests\RuleGroupFormRequest; use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use Input; use Preferences; @@ -52,8 +51,8 @@ class RuleGroupController extends Controller /** - * @param RuleGroupFormRequest $request - * @param RuleRepositoryInterface $repository + * @param RuleGroupFormRequest $request + * @param RuleGroupRepositoryInterface $repository * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ @@ -103,9 +102,9 @@ class RuleGroupController extends Controller } /** - * @param RuleGroupFormRequest $request - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup + * @param RuleGroupFormRequest $request + * @param RuleGroupRepositoryInterface $repository + * @param RuleGroup $ruleGroup * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ @@ -135,8 +134,8 @@ class RuleGroupController extends Controller } /** - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup + * @param RuleGroupRepositoryInterface $repository + * @param RuleGroup $ruleGroup * * @return View */ @@ -156,8 +155,9 @@ class RuleGroupController extends Controller } /** - * @param RuleGroup $ruleGroup - * @param RuleRepositoryInterface $repository + * @param RuleGroupRepositoryInterface $repository + * + * @param RuleGroup $ruleGroup * * @return \Illuminate\Http\RedirectResponse */ @@ -179,8 +179,8 @@ class RuleGroupController extends Controller /** - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup + * @param RuleGroupRepositoryInterface $repository + * @param RuleGroup $ruleGroup * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ @@ -193,8 +193,8 @@ class RuleGroupController extends Controller } /** - * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup + * @param RuleGroupRepositoryInterface $repository + * @param RuleGroup $ruleGroup * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ diff --git a/app/Http/Middleware/Binder.php b/app/Http/Middleware/Binder.php index adb05d03df..088f1e9a8d 100644 --- a/app/Http/Middleware/Binder.php +++ b/app/Http/Middleware/Binder.php @@ -5,6 +5,11 @@ namespace FireflyIII\Http\Middleware; use Closure; use FireflyIII\Support\Domain; +/** + * Class Binder + * + * @package FireflyIII\Http\Middleware + */ class Binder { protected $binders = []; From 168d6f403c9b72c6a55b2416e51d04ca951338ea Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 13:09:27 +0100 Subject: [PATCH 150/276] Shorter method names. --- app/Http/Controllers/RuleGroupController.php | 16 ++++++++-------- app/Http/routes.php | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 1444da86a8..d2312c8c30 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -33,7 +33,7 @@ class RuleGroupController extends Controller /** * @return View */ - public function createRuleGroup() + public function create() { $subTitleIcon = 'fa-clone'; $subTitle = trans('firefly.make_new_rule_group'); @@ -56,7 +56,7 @@ class RuleGroupController extends Controller * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function storeRuleGroup(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository) + public function store(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository) { $data = [ 'title' => $request->input('title'), @@ -85,7 +85,7 @@ class RuleGroupController extends Controller * * @return View */ - public function editRuleGroup(RuleGroup $ruleGroup) + public function edit(RuleGroup $ruleGroup) { $subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]); @@ -108,7 +108,7 @@ class RuleGroupController extends Controller * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function updateRuleGroup(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + public function update(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { $data = [ 'title' => $request->input('title'), @@ -139,7 +139,7 @@ class RuleGroupController extends Controller * * @return View */ - public function deleteRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + public function delete(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { $subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]); @@ -161,7 +161,7 @@ class RuleGroupController extends Controller * * @return \Illuminate\Http\RedirectResponse */ - public function destroyRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + public function destroy(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { $title = $ruleGroup->title; @@ -184,7 +184,7 @@ class RuleGroupController extends Controller * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function upRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + public function up(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { $repository->moveRuleGroupUp($ruleGroup); @@ -198,7 +198,7 @@ class RuleGroupController extends Controller * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function downRuleGroup(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) + public function down(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { $repository->moveRuleGroupDown($ruleGroup); diff --git a/app/Http/routes.php b/app/Http/routes.php index 14a6424893..69d7b901d9 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -255,16 +255,16 @@ Route::group( // rule groups GET - Route::get('/rules/groups/create', ['uses' => 'RuleGroupController@createRuleGroup', 'as' => 'rules.rule-group.create']); - Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleGroupController@editRuleGroup', 'as' => 'rules.rule-group.edit']); - Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleGroupController@deleteRuleGroup', 'as' => 'rules.rule-group.delete']); - Route::get('/rules/groups/up/{ruleGroup}', ['uses' => 'RuleGroupController@upRuleGroup', 'as' => 'rules.rule-group.up']); - Route::get('/rules/groups/down/{ruleGroup}', ['uses' => 'RuleGroupController@downRuleGroup', 'as' => 'rules.rule-group.down']); + Route::get('/rules/groups/create', ['uses' => 'RuleGroupController@create', 'as' => 'rules.rule-group.create']); + Route::get('/rules/groups/edit/{ruleGroup}', ['uses' => 'RuleGroupController@edit', 'as' => 'rules.rule-group.edit']); + Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleGroupController@delete', 'as' => 'rules.rule-group.delete']); + Route::get('/rules/groups/up/{ruleGroup}', ['uses' => 'RuleGroupController@up', 'as' => 'rules.rule-group.up']); + Route::get('/rules/groups/down/{ruleGroup}', ['uses' => 'RuleGroupController@down', 'as' => 'rules.rule-group.down']); // rule groups POST - Route::post('/rules/groups/store', ['uses' => 'RuleGroupController@storeRuleGroup', 'as' => 'rules.rule-group.store']); - Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleGroupController@updateRuleGroup', 'as' => 'rules.rule-group.update']); - Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleGroupController@destroyRuleGroup', 'as' => 'rules.rule-group.destroy']); + Route::post('/rules/groups/store', ['uses' => 'RuleGroupController@store', 'as' => 'rules.rule-group.store']); + Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleGroupController@update', 'as' => 'rules.rule-group.update']); + Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleGroupController@destroy', 'as' => 'rules.rule-group.destroy']); /** * Search Controller From 9cbfbd41dc67690b70ef522c08e2d8dc8695750e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 13:10:34 +0100 Subject: [PATCH 151/276] Shorter method names. --- app/Http/Controllers/RuleController.php | 16 ++++++++-------- app/Http/routes.php | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 05cf1be3e3..256774de3c 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -48,7 +48,7 @@ class RuleController extends Controller * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function storeRule(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup) + public function store(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup) { @@ -88,7 +88,7 @@ class RuleController extends Controller * * @return View */ - public function createRule(RuleGroup $ruleGroup) + public function create(RuleGroup $ruleGroup) { // count for possible present previous entered triggers/actions. $triggerCount = 0; @@ -162,7 +162,7 @@ class RuleController extends Controller * * @return View */ - public function editRule(Rule $rule) + public function edit(Rule $rule) { // count for current rule's triggers/actions. @@ -296,7 +296,7 @@ class RuleController extends Controller * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function updateRule(RuleRepositoryInterface $repository, RuleFormRequest $request, Rule $rule) + public function update(RuleRepositoryInterface $repository, RuleFormRequest $request, Rule $rule) { // process the rule itself: @@ -335,7 +335,7 @@ class RuleController extends Controller * @return View * @internal param RuleRepositoryInterface $repository */ - public function deleteRule(Rule $rule) + public function delete(Rule $rule) { $subTitle = trans('firefly.delete_rule', ['title' => $rule->title]); @@ -354,7 +354,7 @@ class RuleController extends Controller * * @return \Illuminate\Http\RedirectResponse */ - public function destroyRule(RuleRepositoryInterface $repository, Rule $rule) + public function destroy(RuleRepositoryInterface $repository, Rule $rule) { $title = $rule->title; @@ -437,7 +437,7 @@ class RuleController extends Controller * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function upRule(RuleRepositoryInterface $repository, Rule $rule) + public function up(RuleRepositoryInterface $repository, Rule $rule) { $repository->moveRuleUp($rule); @@ -451,7 +451,7 @@ class RuleController extends Controller * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function downRule(RuleRepositoryInterface $repository, Rule $rule) + public function down(RuleRepositoryInterface $repository, Rule $rule) { $repository->moveRuleDown($rule); diff --git a/app/Http/routes.php b/app/Http/routes.php index 69d7b901d9..1b513bb540 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -240,18 +240,18 @@ Route::group( Route::get('/rules', ['uses' => 'RuleController@index', 'as' => 'rules.index']); // rules GET: - Route::get('/rules/create/{ruleGroup}', ['uses' => 'RuleController@createRule', 'as' => 'rules.rule.create']); - Route::get('/rules/rules/up/{rule}', ['uses' => 'RuleController@upRule', 'as' => 'rules.rule.up']); - Route::get('/rules/rules/down/{rule}', ['uses' => 'RuleController@downRule', 'as' => 'rules.rule.down']); - Route::get('/rules/rules/edit/{rule}', ['uses' => 'RuleController@editRule', 'as' => 'rules.rule.edit']); - Route::get('/rules/rules/delete/{rule}', ['uses' => 'RuleController@deleteRule', 'as' => 'rules.rule.delete']); + Route::get('/rules/create/{ruleGroup}', ['uses' => 'RuleController@create', 'as' => 'rules.rule.create']); + Route::get('/rules/rules/up/{rule}', ['uses' => 'RuleController@up', 'as' => 'rules.rule.up']); + Route::get('/rules/rules/down/{rule}', ['uses' => 'RuleController@down', 'as' => 'rules.rule.down']); + Route::get('/rules/rules/edit/{rule}', ['uses' => 'RuleController@edit', 'as' => 'rules.rule.edit']); + Route::get('/rules/rules/delete/{rule}', ['uses' => 'RuleController@delete', 'as' => 'rules.rule.delete']); // rules POST: Route::post('/rules/rules/trigger/reorder/{rule}', ['uses' => 'RuleController@reorderRuleTriggers']); Route::post('/rules/rules/action/reorder/{rule}', ['uses' => 'RuleController@reorderRuleActions']); - Route::post('/rules/store/{ruleGroup}', ['uses' => 'RuleController@storeRule', 'as' => 'rules.rule.store']); - Route::post('/rules/update/{rule}', ['uses' => 'RuleController@updateRule', 'as' => 'rules.rule.update']); - Route::post('/rules/destroy/{rule}', ['uses' => 'RuleController@destroyRule', 'as' => 'rules.rule.destroy']); + Route::post('/rules/store/{ruleGroup}', ['uses' => 'RuleController@store', 'as' => 'rules.rule.store']); + Route::post('/rules/update/{rule}', ['uses' => 'RuleController@update', 'as' => 'rules.rule.update']); + Route::post('/rules/destroy/{rule}', ['uses' => 'RuleController@destroy', 'as' => 'rules.rule.destroy']); // rule groups GET From 651dff0750a142cc9f629efe945575039496c6f4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 13:13:21 +0100 Subject: [PATCH 152/276] Shorter method names. --- .../RuleGroup/RuleGroupRepository.php | 13 ++++++------ .../RuleGroupRepositoryInterface.php | 21 ++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 9c5b3167b5..171468a6ac 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -16,7 +16,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface * * @return boolean */ - public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null) + public function destroy(RuleGroup $ruleGroup, RuleGroup $moveTo = null) { /** @var Rule $rule */ foreach ($ruleGroup->rules as $rule) { @@ -55,7 +55,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface /** * @return Collection */ - public function getRuleGroups() + public function get() { return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get(); } @@ -66,7 +66,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface * * @return bool */ - public function moveRuleGroupUp(RuleGroup $ruleGroup) + public function moveUp(RuleGroup $ruleGroup) { $order = $ruleGroup->order; @@ -87,7 +87,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface * * @return bool */ - public function moveRuleGroupDown(RuleGroup $ruleGroup) + public function moveDown(RuleGroup $ruleGroup) { $order = $ruleGroup->order; @@ -126,6 +126,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface /** * @param RuleGroup $ruleGroup + * * @return bool */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup) @@ -153,7 +154,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface * * @return RuleGroup */ - public function storeRuleGroup(array $data) + public function store(array $data) { $order = $this->getHighestOrderRuleGroup(); @@ -180,7 +181,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface * * @return RuleGroup */ - public function updateRuleGroup(RuleGroup $ruleGroup, array $data) + public function update(RuleGroup $ruleGroup, array $data) { // update the account: $ruleGroup->title = $data['title']; diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index 213ab70766..060f5c5b17 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -6,6 +6,11 @@ namespace FireflyIII\Repositories\RuleGroup; use FireflyIII\Models\RuleGroup; use Illuminate\Support\Collection; +/** + * Interface RuleGroupRepositoryInterface + * + * @package FireflyIII\Repositories\RuleGroup + */ interface RuleGroupRepositoryInterface { /** @@ -14,8 +19,7 @@ interface RuleGroupRepositoryInterface * * @return bool */ - public function destroyRuleGroup(RuleGroup $ruleGroup, RuleGroup $moveTo = null); - + public function destroy(RuleGroup $ruleGroup, RuleGroup $moveTo = null); /** @@ -26,19 +30,21 @@ interface RuleGroupRepositoryInterface /** * @return Collection */ - public function getRuleGroups(); + public function get(); /** * @param RuleGroup $ruleGroup + * * @return bool */ - public function moveRuleGroupUp(RuleGroup $ruleGroup); + public function moveUp(RuleGroup $ruleGroup); /** * @param RuleGroup $ruleGroup + * * @return bool */ - public function moveRuleGroupDown(RuleGroup $ruleGroup); + public function moveDown(RuleGroup $ruleGroup); /** * @return bool @@ -55,8 +61,7 @@ interface RuleGroupRepositoryInterface * * @return RuleGroup */ - public function storeRuleGroup(array $data); - + public function store(array $data); /** * @param RuleGroup $ruleGroup @@ -64,7 +69,7 @@ interface RuleGroupRepositoryInterface * * @return RuleGroup */ - public function updateRuleGroup(RuleGroup $ruleGroup, array $data); + public function update(RuleGroup $ruleGroup, array $data); } \ No newline at end of file From 01792f91e2857d3bb0b9b7b571b4906b33b59c42 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 13:13:33 +0100 Subject: [PATCH 153/276] Code cleanup --- app/Http/Controllers/RuleController.php | 10 +++++----- app/Http/Controllers/RuleGroupController.php | 12 ++++++------ app/Repositories/Account/AccountRepository.php | 4 ++++ app/Repositories/Bill/BillRepository.php | 3 +++ app/Repositories/Rule/RuleRepository.php | 13 +++++++------ .../Rule/RuleRepositoryInterface.php | 17 +++++++++++------ 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 256774de3c..647c55e86d 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -67,7 +67,7 @@ class RuleController extends Controller 'stop_processing' => $request->get('stop_processing'), ]; - $rule = $repository->storeRule($data); + $rule = $repository->store($data); Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title])); Preferences::mark(); @@ -313,7 +313,7 @@ class RuleController extends Controller 'rule-action-stop' => $request->get('rule-action-stop'), 'stop_processing' => intval($request->get('stop_processing')) == 1, ]; - $repository->updateRule($rule, $data); + $repository->update($rule, $data); Session::flash('success', trans('firefly.updated_rule', ['title' => $rule->title])); Preferences::mark(); @@ -358,7 +358,7 @@ class RuleController extends Controller { $title = $rule->title; - $repository->destroyRule($rule); + $repository->destroy($rule); Session::flash('success', trans('firefly.deleted_rule', ['title' => $title])); Preferences::mark(); @@ -439,7 +439,7 @@ class RuleController extends Controller */ public function up(RuleRepositoryInterface $repository, Rule $rule) { - $repository->moveRuleUp($rule); + $repository->moveUp($rule); return redirect(route('rules.index')); @@ -453,7 +453,7 @@ class RuleController extends Controller */ public function down(RuleRepositoryInterface $repository, Rule $rule) { - $repository->moveRuleDown($rule); + $repository->moveDown($rule); return redirect(route('rules.index')); diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index d2312c8c30..7c375cd027 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -64,7 +64,7 @@ class RuleGroupController extends Controller 'user' => Auth::user()->id, ]; - $ruleGroup = $repository->storeRuleGroup($data); + $ruleGroup = $repository->store($data); Session::flash('success', trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])); Preferences::mark(); @@ -116,7 +116,7 @@ class RuleGroupController extends Controller 'active' => intval($request->input('active')) == 1, ]; - $repository->updateRuleGroup($ruleGroup, $data); + $repository->update($ruleGroup, $data); Session::flash('success', trans('firefly.updated_rule_group', ['title' => $ruleGroup->title])); Preferences::mark(); @@ -143,7 +143,7 @@ class RuleGroupController extends Controller { $subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]); - $ruleGroupList = Expandedform::makeSelectList($repository->getRuleGroups(), true); + $ruleGroupList = Expandedform::makeSelectList($repository->get(), true); unset($ruleGroupList[$ruleGroup->id]); // put previous url in session @@ -167,7 +167,7 @@ class RuleGroupController extends Controller $title = $ruleGroup->title; $moveTo = Auth::user()->ruleGroups()->find(intval(Input::get('move_rules_before_delete'))); - $repository->destroyRuleGroup($ruleGroup, $moveTo); + $repository->destroy($ruleGroup, $moveTo); Session::flash('success', trans('firefly.deleted_rule_group', ['title' => $title])); @@ -186,7 +186,7 @@ class RuleGroupController extends Controller */ public function up(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { - $repository->moveRuleGroupUp($ruleGroup); + $repository->moveUp($ruleGroup); return redirect(route('rules.index')); @@ -200,7 +200,7 @@ class RuleGroupController extends Controller */ public function down(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { - $repository->moveRuleGroupDown($ruleGroup); + $repository->moveDown($ruleGroup); return redirect(route('rules.index')); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index a034e4534f..0dd0573754 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -80,6 +80,7 @@ class AccountRepository implements AccountRepositoryInterface return strtolower($account->name); } ); + return $result; } @@ -113,6 +114,7 @@ class AccountRepository implements AccountRepositoryInterface DB::Raw('SUM(`transactions`.`amount`) AS `balance`') ] ); + return $set; } @@ -143,6 +145,7 @@ class AccountRepository implements AccountRepositoryInterface } $result = $query->get(['accounts.*']); + return $result; } @@ -173,6 +176,7 @@ class AccountRepository implements AccountRepositoryInterface ->orderBy('transaction_journals.id', 'DESC') ->take(10) ->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']); + return $set; } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index b07a37f45a..17386d9797 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -146,6 +146,7 @@ class BillRepository implements BillRepositoryInterface ->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.id', 'DESC') ->get(['transaction_journals.*', 'transactions.amount as journalAmount']); + return $set; } @@ -460,6 +461,7 @@ class BillRepository implements BillRepositoryInterface $amount = bcadd($amount, $paid->sum_amount); } } + return $amount; } @@ -515,6 +517,7 @@ class BillRepository implements BillRepositoryInterface $amount = bcadd($amount, $bill->expectedAmount); } } + return $amount; } diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 1521372f3d..5f4be310b5 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -14,7 +14,6 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; -use Illuminate\Support\Collection; /** * Class RuleRepository @@ -48,6 +47,7 @@ class RuleRepository implements RuleRepositoryInterface /** * @param RuleGroup $ruleGroup + * * @return bool */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup) @@ -98,7 +98,7 @@ class RuleRepository implements RuleRepositoryInterface * * @return bool */ - public function moveRuleUp(Rule $rule) + public function moveUp(Rule $rule) { $order = $rule->order; @@ -119,7 +119,7 @@ class RuleRepository implements RuleRepositoryInterface * * @return bool */ - public function moveRuleDown(Rule $rule) + public function moveDown(Rule $rule) { $order = $rule->order; @@ -142,7 +142,7 @@ class RuleRepository implements RuleRepositoryInterface * * @return Rule */ - public function storeRule(array $data) + public function store(array $data) { /** @var RuleGroup $ruleGroup */ $ruleGroup = Auth::user()->ruleGroups()->find($data['rule_group_id']); @@ -213,7 +213,7 @@ class RuleRepository implements RuleRepositoryInterface * * @return bool */ - public function destroyRule(Rule $rule) + public function destroy(Rule $rule) { foreach ($rule->ruleTriggers as $trigger) { $trigger->delete(); @@ -264,9 +264,10 @@ class RuleRepository implements RuleRepositoryInterface /** * @param Rule $rule * @param array $data + * * @return Rule */ - public function updateRule(Rule $rule, array $data) + public function update(Rule $rule, array $data) { // update rule: $rule->active = $data['active']; diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 80a3e832b9..7843c8c72a 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -13,7 +13,6 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; -use Illuminate\Support\Collection; /** * Interface RuleRepositoryInterface @@ -27,7 +26,7 @@ interface RuleRepositoryInterface * * @return bool */ - public function destroyRule(Rule $rule); + public function destroy(Rule $rule); /** * @param RuleGroup $ruleGroup @@ -39,6 +38,7 @@ interface RuleRepositoryInterface /** * @param Rule $rule * @param array $ids + * * @return bool */ public function reorderRuleTriggers(Rule $rule, array $ids); @@ -46,41 +46,46 @@ interface RuleRepositoryInterface /** * @param Rule $rule * @param array $ids + * * @return bool */ public function reorderRuleActions(Rule $rule, array $ids); /** * @param RuleGroup $ruleGroup + * * @return bool */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup); /** * @param Rule $rule + * * @return bool */ - public function moveRuleUp(Rule $rule); + public function moveUp(Rule $rule); /** * @param Rule $rule * @param array $data + * * @return Rule */ - public function updateRule(Rule $rule, array $data); + public function update(Rule $rule, array $data); /** * @param Rule $rule + * * @return bool */ - public function moveRuleDown(Rule $rule); + public function moveDown(Rule $rule); /** * @param array $data * * @return Rule */ - public function storeRule(array $data); + public function store(array $data); /** * @param Rule $rule From 2afb455bac7a0d4a20701234f3895eda88724128 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 13:40:39 +0100 Subject: [PATCH 154/276] New translations. --- resources/lang/en_US/firefly.php | 2 +- resources/lang/nl_NL/breadcrumbs.php | 0 resources/lang/nl_NL/config.php | 0 resources/lang/nl_NL/firefly.php | 954 +++++++++++++++------------ resources/lang/nl_NL/form.php | 7 + resources/lang/nl_NL/help.php | 131 ++-- resources/lang/nl_NL/list.php | 0 resources/lang/nl_NL/pagination.php | 0 resources/lang/nl_NL/passwords.php | 0 resources/lang/nl_NL/validation.php | 2 + 10 files changed, 599 insertions(+), 497 deletions(-) mode change 100644 => 100755 resources/lang/nl_NL/breadcrumbs.php mode change 100644 => 100755 resources/lang/nl_NL/config.php mode change 100644 => 100755 resources/lang/nl_NL/firefly.php mode change 100644 => 100755 resources/lang/nl_NL/form.php mode change 100644 => 100755 resources/lang/nl_NL/help.php mode change 100644 => 100755 resources/lang/nl_NL/list.php mode change 100644 => 100755 resources/lang/nl_NL/pagination.php mode change 100644 => 100755 resources/lang/nl_NL/passwords.php mode change 100644 => 100755 resources/lang/nl_NL/validation.php diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index d354d410c7..9beff0f8e6 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -40,7 +40,7 @@ return [ // rules 'rules' => 'Rules', - 'rules_explanation' => 'Here is going to be text', + 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', 'rule_name' => 'Name of rule', 'rule_triggers' => 'Rule triggers when', 'rule_actions' => 'Rule will', diff --git a/resources/lang/nl_NL/breadcrumbs.php b/resources/lang/nl_NL/breadcrumbs.php old mode 100644 new mode 100755 diff --git a/resources/lang/nl_NL/config.php b/resources/lang/nl_NL/config.php old mode 100644 new mode 100755 diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php old mode 100644 new mode 100755 index c34aef8a41..7644571945 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -2,478 +2,580 @@ return [ // general stuff: - 'language_incomplete' => 'Deze taal is nog niet helemaal af', - 'test' => 'Nederlands geselecteerd!', - 'close' => 'Sluiten', - 'pleaseHold' => 'Momentje...', - 'actions' => 'Acties', - 'edit' => 'Wijzig', - 'delete' => 'Verwijder', - 'welcomeBack' => 'Hoe staat het er voor?', - 'everything' => 'Alles', - 'customRange' => 'Zelf bereik kiezen', - 'apply' => 'Go', - 'cancel' => 'Annuleren', - 'from' => 'Van', - 'to' => 'Tot', - 'total_sum' => 'Totale som', - 'period_sum' => 'Som van periode', - 'showEverything' => 'Laat alles zien', - 'never' => 'Nooit', - 'search_results_for' => 'Zoekresultaten voor ":query"', - 'bounced_error' => 'Het emailtje naar :email kwam nooit aan.', - 'deleted_error' => 'Deze gegevens zijn niet correct.', - 'general_blocked_error' => 'Je account is uitgeschakeld, je kan helaas niet inloggen.', - 'removed_amount' => ':amount weggehaald', - 'added_amount' => ':amount toegevoegd', - 'asset_account_role_help' => 'Voorkeuren die voortkomen uit je keuze hier kan je later aangeven.', - 'Opening balance' => 'Startsaldo', - 'create_new_stuff' => 'Nieuw', - 'new_withdrawal' => 'Nieuwe uitgave', - 'new_deposit' => 'Nieuwe inkomsten', - 'new_transfer' => 'Nieuwe overschrijving', - 'new_asset_account' => 'Nieuwe betaalrekening', - 'new_expense_account' => 'Nieuwe crediteur', - 'new_revenue_account' => 'Nieuwe debiteur', - 'new_budget' => 'Nieuw budget', - 'new_bill' => 'Nieuw contract', + 'language_incomplete' => 'Deze taal is nog niet helemaal af', + 'test' => 'Nederlands geselecteerd!', + 'close' => 'Sluiten', + 'pleaseHold' => 'Momentje...', + 'actions' => 'Acties', + 'edit' => 'Wijzig', + 'delete' => 'Verwijder', + 'welcomeBack' => 'Hoe staat het er voor?', + 'everything' => 'Alles', + 'customRange' => 'Zelf bereik kiezen', + 'apply' => 'Go', + 'cancel' => 'Annuleren', + 'from' => 'Van', + 'to' => 'Tot', + 'total_sum' => 'Totale som', + 'period_sum' => 'Som van periode', + 'showEverything' => 'Laat alles zien', + 'never' => 'Nooit', + 'search_results_for' => 'Zoekresultaten voor ":query"', + 'bounced_error' => 'Het emailtje naar :email kwam nooit aan.', + 'deleted_error' => 'Deze gegevens zijn niet correct.', + 'general_blocked_error' => 'Je account is uitgeschakeld, je kan helaas niet inloggen.', + 'removed_amount' => ':amount weggehaald', + 'added_amount' => ':amount toegevoegd', + 'asset_account_role_help' => 'Voorkeuren die voortkomen uit je keuze hier kan je later aangeven.', + 'Opening balance' => 'Startsaldo', + 'create_new_stuff' => 'Nieuw', + 'new_withdrawal' => 'Nieuwe uitgave', + 'new_deposit' => 'Nieuwe inkomsten', + 'new_transfer' => 'Nieuwe overschrijving', + 'new_asset_account' => 'Nieuwe betaalrekening', + 'new_expense_account' => 'Nieuwe crediteur', + 'new_revenue_account' => 'Nieuwe debiteur', + 'new_budget' => 'Nieuw budget', + 'new_bill' => 'Nieuw contract', + + // rules + 'rules' => 'Regels', + 'rules_explanation' => 'Hier kan je regels instellen. Regels worden in werking gesteld als je een bij-, afschrijving of overboeking maakt (of verandert). Als die transactie bepaalde eigenschappen heeft (zogenaamde "triggers") zal Firefly de bijbehorende "acties" uitvoeren. Gecombineerd kan je Firefly op een bepaalde manier laten reageren op nieuwe transacties.', + 'rule_name' => 'Regelnaam', + 'rule_triggers' => 'Regel reageert op', + 'rule_actions' => 'Regel zal dan', + 'new_rule' => 'Nieuwe regel', + 'new_rule_group' => 'Nieuwe regelgroep', + 'rule_priority_up' => 'Geef regel meer prioriteit', + 'rule_priority_down' => 'Geef regel minder prioriteit', + 'make_new_rule_group' => 'Maak nieuwe regelgroep', + 'store_new_rule_group' => 'Sla nieuwe regelgroep op', + 'created_new_rule_group' => 'Nieuwe regelgroep ":title" opgeslagen!', + 'updated_rule_group' => 'Regelgroep ":title" geüpdatet.', + 'edit_rule_group' => 'Wijzig regelgroep ":title"', + 'delete_rule_group' => 'Verwijder regelgroep ":title"', + 'deleted_rule_group' => 'Regelgroep ":title" verwijderd', + 'update_rule_group' => 'Wijzig regelgroep', + 'no_rules_in_group' => 'Er zijn geen regels in deze groep', + 'move_rule_group_up' => 'Verplaats regelgroep omhoog', + 'move_rule_group_down' => 'Verplaats regelgroep omlaag', + 'save_rules_by_moving' => 'Red deze regel(s) van de ondergang door ze te verplaatsen naar een andere regelgroep:', + 'make_new_rule' => 'Maak een nieuwe regel in regelgroep ":title"', + 'rule_help_stop_processing' => 'Zet hier een vinkje om latere regels in deze groep te negeren.', + 'rule_help_active' => 'Niet actieve regels zullen nooit worden gecontroleerd.', + 'stored_new_rule' => 'Nieuwe regel ":title" opgeslagen', + 'deleted_rule' => 'Regel ":title" verwijderd', + 'store_new_rule' => 'Sla nieuwe regel op', + 'updated_rule' => 'Regel ":title" geüpdatet', + + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger bij waarde', + 'stop_processing_other_triggers' => 'Reageer niet meer op andere triggers', + 'add_rule_trigger' => 'Nieuwe trigger toevoegen', + 'action' => 'Actie', + 'action_value' => 'Actie-waarde', + 'stop_executing_other_actions' => 'Voer verdere acties niet uit', + 'add_rule_action' => 'Nieuwe actie toevoegen', + 'edit_rule' => 'Wijzig regel ":title"', + 'update_rule' => 'Werk regel bij', + + // actions and triggers + 'rule_trigger_user_action' => 'Gebruikersactie is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Bronrekeningnaam begint met ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Bronrekeningnaam eindigt op ":trigger_value"', + 'rule_trigger_from_account_is' => 'Bronrekeningnaam is ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Bronrekeningnaam bevat ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Doelrekeningnaam begint met ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Doelrekeningnaam eindigt op ":trigger_value"', + 'rule_trigger_to_account_is' => 'Doelrekeningnaam is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Doelrekeningnaam bevat ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transactiesoort is ":trigger_value" (Engels)', + 'rule_trigger_amount_less' => 'Bedrag is minder dan :trigger_value', + 'rule_trigger_amount_exactly' => 'Bedrag is :trigger_value', + 'rule_trigger_amount_more' => 'Bedrag is meer dan :trigger_value', + 'rule_trigger_description_starts' => 'Omschrijving begint met ":trigger_value"', + 'rule_trigger_description_ends' => 'Omschrijving eindigt op ":trigger_value"', + 'rule_trigger_description_contains' => 'Beschrijving bevat ":trigger_value"', + 'rule_trigger_description_is' => 'Beschrijving is ":trigger_value"', + + 'rule_trigger_from_account_starts_choice' => 'Bronrekening naam begint met..', + 'rule_trigger_from_account_ends_choice' => 'Bronrekening eindigt op..', + 'rule_trigger_from_account_is_choice' => 'Bronrekening is..', + 'rule_trigger_from_account_contains_choice' => 'Bronrekening bevat..', + 'rule_trigger_to_account_starts_choice' => 'Doelrekeningnaam begint met..', + 'rule_trigger_to_account_ends_choice' => 'Doelrekeningnaam eindigt op..', + 'rule_trigger_to_account_is_choice' => 'Doelrekeningnaam is..', + 'rule_trigger_to_account_contains_choice' => 'Doelrekeningnaam bevat..', + 'rule_trigger_transaction_type_choice' => 'Transactietype is..', + 'rule_trigger_amount_less_choice' => 'Bedrag is minder dan..', + 'rule_trigger_amount_exactly_choice' => 'Bedrag is..', + 'rule_trigger_amount_more_choice' => 'Bedrag is meer dan..', + 'rule_trigger_description_starts_choice' => 'Omschrijving begint met..', + 'rule_trigger_description_ends_choice' => 'Omschrijving eindigt op..', + 'rule_trigger_description_contains_choice' => 'Omschrijving bevat..', + 'rule_trigger_description_is_choice' => 'Omschrijving is..', + + 'rule_trigger_store_journal' => 'Als een transactie wordt gemaakt', + 'rule_trigger_update_journal' => 'Als een transactie wordt bewerkt', + + 'rule_action_set_category' => 'Verander categorie naar ":action_value"', + 'rule_action_clear_category' => 'Maak categorie-veld leeg', + 'rule_action_set_budget' => 'Sla op onder budget ":action_value"', + 'rule_action_clear_budget' => 'Sla op zonder budget', + 'rule_action_add_tag' => 'Voeg tag ":action_value" toe', + 'rule_action_remove_tag' => 'Haal tag ":action_value" weg', + 'rule_action_remove_all_tags' => 'Haal alle tags weg', + 'rule_action_set_description' => 'Geef omschrijving ":action_value"', + 'rule_action_append_description' => 'Zet ":action_value" voor de omschrijving', + 'rule_action_prepend_description' => 'Zet ":action_value" voor de omschrijving', + + 'rule_action_set_category_choice' => 'Geef categorie..', + 'rule_action_clear_category_choice' => 'Geef geen categorie', + 'rule_action_set_budget_choice' => 'Sla op onder budget..', + 'rule_action_clear_budget_choice' => 'Maak budget-veld leeg', + 'rule_action_add_tag_choice' => 'Voeg tag toe..', + 'rule_action_remove_tag_choice' => 'Haal tag weg..', + 'rule_action_remove_all_tags_choice' => 'Haal alle tags weg', + 'rule_action_set_description_choice' => 'Geef omschrijving..', + 'rule_action_append_description_choice' => 'Zet .. achter de omschrijving', + 'rule_action_prepend_description_choice' => 'Zet .. voor de omschrijving', // tags - 'store_new_tag' => 'Sla tag op', - 'update_tag' => 'Sla wijzigingen op', - 'no_location_set' => 'Zonder plaats', - 'meta_data' => 'Metagegevens', - 'location' => 'Plaats', + 'store_new_tag' => 'Sla tag op', + 'update_tag' => 'Sla wijzigingen op', + 'no_location_set' => 'Zonder plaats', + 'meta_data' => 'Metagegevens', + 'location' => 'Plaats', // preferences - 'pref_home_screen_accounts' => 'Voorpaginarekeningen', - 'pref_home_screen_accounts_help' => 'Welke betaalrekeningen wil je op de voorpagina zien?', - 'pref_budget_settings' => 'Budgetinstellingen', - 'pref_budget_settings_help' => 'Wat is het maximale bedrag dat je voor een budget kan instellen?', - 'pref_view_range' => 'Bereik', - 'pref_view_range_help' => 'Sommige pagina\'s springen naar een standaard bereik. Welk bereik heeft jouw voorkeur?', - 'pref_1D' => 'Eén dag', - 'pref_1W' => 'Eén week', - 'pref_1M' => 'Eén maand', - 'pref_3M' => 'Drie maanden (kwartaal)', - 'pref_6M' => 'Zes maanden', - 'pref_languages' => 'Talen', - 'pref_languages_help' => 'Firefly III ondersteunt meerdere talen. Welke heeft jouw voorkeur?', - 'pref_save_settings' => 'Instellingen opslaan', + 'pref_home_screen_accounts' => 'Voorpaginarekeningen', + 'pref_home_screen_accounts_help' => 'Welke betaalrekeningen wil je op de voorpagina zien?', + 'pref_budget_settings' => 'Budgetinstellingen', + 'pref_budget_settings_help' => 'Wat is het maximale bedrag dat je voor een budget kan instellen?', + 'pref_view_range' => 'Bereik', + 'pref_view_range_help' => 'Sommige pagina\'s springen naar een standaard bereik. Welk bereik heeft jouw voorkeur?', + 'pref_1D' => 'Eén dag', + 'pref_1W' => 'Eén week', + 'pref_1M' => 'Eén maand', + 'pref_3M' => 'Drie maanden (kwartaal)', + 'pref_6M' => 'Zes maanden', + 'pref_languages' => 'Talen', + 'pref_languages_help' => 'Firefly III ondersteunt meerdere talen. Welke heeft jouw voorkeur?', + 'pref_save_settings' => 'Instellingen opslaan', // profile: - 'change_your_password' => 'Verander je wachtwoord', - 'delete_account' => 'Verwijder je account', - 'current_password' => 'Huidige wachtwoord', - 'new_password' => 'Nieuw wachtwoord', - 'new_password_again' => 'Nieuw wachtwoord (bevestiging)', - 'delete_your_account' => 'Verwijder je account', - 'delete_your_account_help' => 'Als je je account verwijdert worden ook al je rekeningen, transacties en alle andere zaken verwijderd. Alles is dan WEG.', - 'delete_your_account_password' => 'Voer je wachtwoord in om door te gaan.', - 'password' => 'Wachtwoord', - 'are_you_sure' => 'Zeker weten? Je kan niet meer terug!', - 'delete_account_button' => 'VERWIJDER je account', - 'invalid_current_password' => 'Huidige wachtwoord is niet geldig!', - 'password_changed' => 'Je wachtwoord is veranderd!', - 'should_change' => 'Vul ook echt een ander wachtwoord in.', - 'invalid_password' => 'Ongeldig wachtwoord!', + 'change_your_password' => 'Verander je wachtwoord', + 'delete_account' => 'Verwijder je account', + 'current_password' => 'Huidige wachtwoord', + 'new_password' => 'Nieuw wachtwoord', + 'new_password_again' => 'Nieuw wachtwoord (bevestiging)', + 'delete_your_account' => 'Verwijder je account', + 'delete_your_account_help' => 'Als je je account verwijdert worden ook al je rekeningen, transacties en alle andere zaken verwijderd. Alles is dan WEG.', + 'delete_your_account_password' => 'Voer je wachtwoord in om door te gaan.', + 'password' => 'Wachtwoord', + 'are_you_sure' => 'Zeker weten? Je kan niet meer terug!', + 'delete_account_button' => 'VERWIJDER je account', + 'invalid_current_password' => 'Huidige wachtwoord is niet geldig!', + 'password_changed' => 'Je wachtwoord is veranderd!', + 'should_change' => 'Vul ook echt een ander wachtwoord in.', + 'invalid_password' => 'Ongeldig wachtwoord!', // attachments - 'nr_of_attachments' => 'Eén bijlage|:count bijlagen', - 'attachments' => 'Bijlagen', - 'edit_attachment' => 'Wijzig bijlage ":name"', - 'update_attachment' => 'Update bijlage', - 'delete_attachment' => 'Verwijder bijlage ":name"', - 'attachment_deleted' => 'Bijlage ":name" verwijderd', - 'upload_max_file_size' => 'Maximale grootte: :size', + 'nr_of_attachments' => 'Eén bijlage|:count bijlagen', + 'attachments' => 'Bijlagen', + 'edit_attachment' => 'Wijzig bijlage ":name"', + 'update_attachment' => 'Update bijlage', + 'delete_attachment' => 'Verwijder bijlage ":name"', + 'attachment_deleted' => 'Bijlage ":name" verwijderd', + 'upload_max_file_size' => 'Maximale grootte: :size', // tour: - 'prev' => 'Vorige', - 'next' => 'Volgende', - 'end-tour' => 'Einde', - 'pause' => 'Pauze', + 'prev' => 'Vorige', + 'next' => 'Volgende', + 'end-tour' => 'Einde', + 'pause' => 'Pauze', // transaction index - 'title_expenses' => 'Uitgaven', - 'title_withdrawal' => 'Uitgaven', - 'title_revenue' => 'Inkomsten', - 'title_deposit' => 'Inkomsten', - 'title_transfer' => 'Overboekingen', - 'title_transfers' => 'Overboekingen', + 'title_expenses' => 'Uitgaven', + 'title_withdrawal' => 'Uitgaven', + 'title_revenue' => 'Inkomsten', + 'title_deposit' => 'Inkomsten', + 'title_transfer' => 'Overboekingen', + 'title_transfers' => 'Overboekingen', // csv import: - 'csv_import' => 'Importeer CSV-bestand', - 'csv' => 'CSV', - 'csv_index_title' => 'Upload en importeer een kommagescheiden tekstbestand', - 'csv_define_column_roles' => 'Bepaal kolominhoud', - 'csv_map_values' => 'Leg relaties met kolomwaardes', - 'csv_download_config' => 'Download CSV configuratiebestand.', - 'csv_index_text' => 'Met deze (en de komende) pagina\'s kan je kommagescheiden tekstbestanden importeren. Deze tool is gebaseerd op de prachtige tool van Atlassian. Om te beginnen selecteer je jouw tekstbestand bij "CSV-bestand". Als je hulp nodig hebt, klik dan op het -icoontje rechtsboven.', - 'csv_index_beta_warning' => 'Deze tool is nog erg experimenteel. Wees dus voorzichtig.', - 'csv_header_help' => 'Zet hier een vinkje als de eerste rij van het CSV-bestand de namen van de kolommen bevat', - 'csv_date_help' => 'Het gebruikte datumformaat in jouw bestand. Gebruik het formaat zoals deze pagina het uitlegt (Engels). Het standaardformaat kan omgaan met data zoals deze: :dateExample.', - 'csv_csv_file_help' => 'Voer hier je kommagescheiden tekstbestand in. Je kan er maar één tegelijkertijd invoeren.', - 'csv_csv_config_file_help' => 'Voer hier je configuratiebestand in. Als je deze niet hebt, geen zorgen. Latere stappen leggen dit uit.', - 'csv_upload_button' => 'Begin de import', - 'csv_column_roles_title' => 'Bepaal de inhoud van elke kolom', - 'csv_column_roles_text' => 'Firefly kan niet automatisch ontdekken wat elke kolom betekent. Je moet het zelf aangeven. Gebruik de voorbeeldgegevens als je het ook niet zeker weet. Klik op het vraagteken-icoontje (rechtsboven) om te ontdekken wat elke kolomsoort precies is. Als de kolominhoud een directe relatie heeft met gegevens die al in Firefly staan, gebruik dan het vinkje. Tijdens de volgende stap komt Firefly hier dan op terug.', - 'csv_column_roles_table' => 'Kolominhoud', - 'csv_column' => 'CSV-kolom', - 'csv_column_name' => 'CSV-kolomnaam', - 'csv_column_example' => 'Voorbeeldgegevens', - 'csv_column_role' => 'Kolom bevat?', - 'csv_do_map_value' => 'Directe relatie?', - 'csv_continue' => 'Naar de volgende stap', - 'csv_go_back' => 'Terug naar de vorige stap', - 'csv_map_title' => 'Leg relaties met kolomwaardes', - 'csv_map_text' => 'Sommige kolommen bevatten waardes die misschien al in Firefly bestaan. Selecteer hier de juiste combinaties zodat het importeren netjes aansluit bij je huidige gegevens.', - 'csv_field_value' => 'Veldwaarde', - 'csv_field_mapped_to' => 'Is gelijk aan', - 'csv_do_not_map' => 'Geen relatie', - 'csv_download_config_title' => 'Download importconfiguratie', - 'csv_download_config_text' => 'Alles wat je nu hebt zitten instellen kan je downloaden als configuratiebestand voor de volgende keer. Klik op de knop om dit te doen.', - 'csv_more_information_text' => 'Ook als het importeren fout gaat is dit bestand handig. Na het importeren krijg je nogmaals de gelegenheid dit bestand te downloaden.', - 'csv_do_download_config' => 'Download het configuratiebestand', - 'csv_empty_description' => '(geen beschrijving)', - 'csv_upload_form' => 'CSV upload formulier', - 'csv_index_unsupported_warning' => 'Het volgende wordt nog niet ondersteund:', - 'csv_unsupported_map' => 'De tool kan de kolom ":columnRole" niet koppelen aan bestaande gegevens in de database.', - 'csv_unsupported_value' => 'Firefly kan niet omgaan met kolommen gemarkeerd als ":columnRole".', - 'csv_cannot_store_value' => 'Firefly heeft geen ruimte gereserveerd voor kolommen gemarkeert als ":columnRole" en kan ze helaas niet verwerken.', - 'csv_process_title' => 'Het importeren is klaar', - 'csv_process_text' => ':rows rijen zijn verwerkt.', - 'csv_row' => 'Rij', - 'csv_import_with_errors' => 'Er ging één ding fout.|Er gingen :errors dingen fout.', - 'csv_error_see_logs' => 'De logboeken bevatten mogelijk meer details.', - 'csv_process_new_entries' => 'Firefly heeft :imported nieuwe transactie(s) gemaakt.', - 'csv_start_over' => 'Begin opnieuw', - 'csv_to_index' => 'Naar de index', - 'csv_upload_not_writeable' => 'Kan niet naar onderstaand pad schrijven. Kan dus niet uploaden.', - 'csv_column__ignore' => '(negeer deze kolom)', - 'csv_column_account-iban' => 'Betaalrekening (IBAN)', - 'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', - 'csv_column_account-name' => 'Betaalrekeningnaam', - 'csv_column_amount' => 'Bedrag', - 'csv_column_bill-id' => 'Contract (ID gelijk aan Firefly)', - 'csv_column_bill-name' => 'Contractnaam', - 'csv_column_budget-id' => 'Budget (ID gelijk aan Firefly)', - 'csv_column_budget-name' => 'Budgetnaam', - 'csv_column_category-id' => 'Categorie (ID gelijk aan Firefly)', - 'csv_column_category-name' => 'Categorienaam', - 'csv_column_currency-code' => 'Valutacode (ISO 4217)', - 'csv_column_currency-id' => 'Valuta (ID gelijk aan Firefly)', - 'csv_column_currency-name' => 'Valutanaam', - 'csv_column_currency-symbol' => 'Valuta', - 'csv_column_date-rent' => 'Datum (renteberekening)', - 'csv_column_date-transaction' => 'Datum (transactie)', - 'csv_column_description' => 'Beschrijving', - 'csv_column_opposing-iban' => 'Tegenrekening (IBAN)', - 'csv_column_opposing-id' => 'Tegenrekening (ID gelijk aan Firefly)', - 'csv_column_opposing-name' => 'Tegenrekeningnaam', - 'csv_column_rabo-debet-credit' => 'Rabobankspecifiek bij/af indicator', - 'csv_column_sepa-ct-id' => 'SEPA transactienummer', - 'csv_column_sepa-ct-op' => 'SEPA tegenrekeningnummer', - 'csv_column_sepa-db' => 'SEPA "direct debet"-nummer', - 'csv_column_tags-comma' => 'Tags (kommagescheiden)', - 'csv_column_tags-space' => 'Tags (spatiegescheiden)', - 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', - 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', - 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', - 'csv_date_parse_error' => 'Firefly kan van ":value" geen datum maken, gegeven het formaat ":format". Weet je zeker dat je CSV goed is?', + 'csv_import' => 'Importeer CSV-bestand', + 'csv' => 'CSV', + 'csv_index_title' => 'Upload en importeer een kommagescheiden tekstbestand', + 'csv_define_column_roles' => 'Bepaal kolominhoud', + 'csv_map_values' => 'Leg relaties met kolomwaardes', + 'csv_download_config' => 'Download CSV configuratiebestand.', + 'csv_index_text' => 'Met deze (en de komende) pagina\'s kan je kommagescheiden tekstbestanden importeren. Deze tool is gebaseerd op de prachtige tool van Atlassian. Om te beginnen selecteer je jouw tekstbestand bij "CSV-bestand". Als je hulp nodig hebt, klik dan op het -icoontje rechtsboven.', + 'csv_index_beta_warning' => 'Deze tool is nog erg experimenteel. Wees dus voorzichtig.', + 'csv_header_help' => 'Zet hier een vinkje als de eerste rij van het CSV-bestand de namen van de kolommen bevat', + 'csv_date_help' => 'Het gebruikte datumformaat in jouw bestand. Gebruik het formaat zoals deze pagina het uitlegt (Engels). Het standaardformaat kan omgaan met data zoals deze: :dateExample.', + 'csv_csv_file_help' => 'Voer hier je kommagescheiden tekstbestand in. Je kan er maar één tegelijkertijd invoeren.', + 'csv_csv_config_file_help' => 'Voer hier je configuratiebestand in. Als je deze niet hebt, geen zorgen. Latere stappen leggen dit uit.', + 'csv_upload_button' => 'Begin de import', + 'csv_column_roles_title' => 'Bepaal de inhoud van elke kolom', + 'csv_column_roles_text' => 'Firefly kan niet automatisch ontdekken wat elke kolom betekent. Je moet het zelf aangeven. Gebruik de voorbeeldgegevens als je het ook niet zeker weet. Klik op het vraagteken-icoontje (rechtsboven) om te ontdekken wat elke kolomsoort precies is. Als de kolominhoud een directe relatie heeft met gegevens die al in Firefly staan, gebruik dan het vinkje. Tijdens de volgende stap komt Firefly hier dan op terug.', + 'csv_column_roles_table' => 'Kolominhoud', + 'csv_column' => 'CSV-kolom', + 'csv_column_name' => 'CSV-kolomnaam', + 'csv_column_example' => 'Voorbeeldgegevens', + 'csv_column_role' => 'Kolom bevat?', + 'csv_do_map_value' => 'Directe relatie?', + 'csv_continue' => 'Naar de volgende stap', + 'csv_go_back' => 'Terug naar de vorige stap', + 'csv_map_title' => 'Leg relaties met kolomwaardes', + 'csv_map_text' => 'Sommige kolommen bevatten waardes die misschien al in Firefly bestaan. Selecteer hier de juiste combinaties zodat het importeren netjes aansluit bij je huidige gegevens.', + 'csv_field_value' => 'Veldwaarde', + 'csv_field_mapped_to' => 'Is gelijk aan', + 'csv_do_not_map' => 'Geen relatie', + 'csv_download_config_title' => 'Download importconfiguratie', + 'csv_download_config_text' => 'Alles wat je nu hebt zitten instellen kan je downloaden als configuratiebestand voor de volgende keer. Klik op de knop om dit te doen.', + 'csv_more_information_text' => 'Ook als het importeren fout gaat is dit bestand handig. Na het importeren krijg je nogmaals de gelegenheid dit bestand te downloaden.', + 'csv_do_download_config' => 'Download het configuratiebestand', + 'csv_empty_description' => '(geen beschrijving)', + 'csv_upload_form' => 'CSV upload formulier', + 'csv_index_unsupported_warning' => 'Het volgende wordt nog niet ondersteund:', + 'csv_unsupported_map' => 'De tool kan de kolom ":columnRole" niet koppelen aan bestaande gegevens in de database.', + 'csv_unsupported_value' => 'Firefly kan niet omgaan met kolommen gemarkeerd als ":columnRole".', + 'csv_cannot_store_value' => 'Firefly heeft geen ruimte gereserveerd voor kolommen gemarkeert als ":columnRole" en kan ze helaas niet verwerken.', + 'csv_process_title' => 'Het importeren is klaar', + 'csv_process_text' => ':rows rijen zijn verwerkt.', + 'csv_row' => 'Rij', + 'csv_import_with_errors' => 'Er ging één ding fout.|Er gingen :errors dingen fout.', + 'csv_error_see_logs' => 'De logboeken bevatten mogelijk meer details.', + 'csv_process_new_entries' => 'Firefly heeft :imported nieuwe transactie(s) gemaakt.', + 'csv_start_over' => 'Begin opnieuw', + 'csv_to_index' => 'Naar de index', + 'csv_upload_not_writeable' => 'Kan niet naar onderstaand pad schrijven. Kan dus niet uploaden.', + 'csv_column__ignore' => '(negeer deze kolom)', + 'csv_column_account-iban' => 'Betaalrekening (IBAN)', + 'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', + 'csv_column_account-name' => 'Betaalrekeningnaam', + 'csv_column_amount' => 'Bedrag', + 'csv_column_bill-id' => 'Contract (ID gelijk aan Firefly)', + 'csv_column_bill-name' => 'Contractnaam', + 'csv_column_budget-id' => 'Budget (ID gelijk aan Firefly)', + 'csv_column_budget-name' => 'Budgetnaam', + 'csv_column_category-id' => 'Categorie (ID gelijk aan Firefly)', + 'csv_column_category-name' => 'Categorienaam', + 'csv_column_currency-code' => 'Valutacode (ISO 4217)', + 'csv_column_currency-id' => 'Valuta (ID gelijk aan Firefly)', + 'csv_column_currency-name' => 'Valutanaam', + 'csv_column_currency-symbol' => 'Valuta', + 'csv_column_date-rent' => 'Datum (renteberekening)', + 'csv_column_date-transaction' => 'Datum (transactie)', + 'csv_column_description' => 'Beschrijving', + 'csv_column_opposing-iban' => 'Tegenrekening (IBAN)', + 'csv_column_opposing-id' => 'Tegenrekening (ID gelijk aan Firefly)', + 'csv_column_opposing-name' => 'Tegenrekeningnaam', + 'csv_column_rabo-debet-credit' => 'Rabobankspecifiek bij/af indicator', + 'csv_column_sepa-ct-id' => 'SEPA transactienummer', + 'csv_column_sepa-ct-op' => 'SEPA tegenrekeningnummer', + 'csv_column_sepa-db' => 'SEPA "direct debet"-nummer', + 'csv_column_tags-comma' => 'Tags (kommagescheiden)', + 'csv_column_tags-space' => 'Tags (spatiegescheiden)', + 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', + 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', + 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', + 'csv_date_parse_error' => 'Firefly kan van ":value" geen datum maken, gegeven het formaat ":format". Weet je zeker dat je CSV goed is?', // create new stuff: - 'create_new_withdrawal' => 'Nieuwe uitgave', - 'create_new_deposit' => 'Nieuwe inkomsten', - 'create_new_transfer' => 'Nieuwe overschrijving', - 'create_new_asset' => 'Nieuwe betaalrekening', - 'create_new_expense' => 'Nieuwe crediteur', - 'create_new_revenue' => 'Nieuwe debiteur', - 'create_new_piggy_bank' => 'Nieuw spaarpotje', - 'create_new_bill' => 'Nieuw contract', + 'create_new_withdrawal' => 'Nieuwe uitgave', + 'create_new_deposit' => 'Nieuwe inkomsten', + 'create_new_transfer' => 'Nieuwe overschrijving', + 'create_new_asset' => 'Nieuwe betaalrekening', + 'create_new_expense' => 'Nieuwe crediteur', + 'create_new_revenue' => 'Nieuwe debiteur', + 'create_new_piggy_bank' => 'Nieuw spaarpotje', + 'create_new_bill' => 'Nieuw contract', // currencies: - 'create_currency' => 'Voeg nieuwe valuta toe', - 'edit_currency' => 'Wijzig valuta ":name"', - 'store_currency' => 'Sla nieuwe valuta op', - 'update_currency' => 'Wijzig valuta', + 'create_currency' => 'Voeg nieuwe valuta toe', + 'edit_currency' => 'Wijzig valuta ":name"', + 'store_currency' => 'Sla nieuwe valuta op', + 'update_currency' => 'Wijzig valuta', // new user: - 'submit' => 'Invoeren', - 'getting_started' => 'Aan de start!', - 'to_get_started' => 'Begin met de naam van de bank waar je je betaalrekening hebt, en het saldo van die rekening.', - 'savings_balance_text' => 'Voer ook het saldo van je spaarrekening in, als je die hebt.', - 'cc_balance_text' => 'Als je een credit card hebt, vul dan hier je credit cardlimiet in.', + 'submit' => 'Invoeren', + 'getting_started' => 'Aan de start!', + 'to_get_started' => 'Begin met de naam van de bank waar je je betaalrekening hebt, en het saldo van die rekening.', + 'savings_balance_text' => 'Voer ook het saldo van je spaarrekening in, als je die hebt.', + 'cc_balance_text' => 'Als je een credit card hebt, vul dan hier je credit cardlimiet in.', // forms: - 'mandatoryFields' => 'Verplichte velden', - 'optionalFields' => 'Optionele velden', - 'options' => 'Opties', - 'something' => 'Iets!', + 'mandatoryFields' => 'Verplichte velden', + 'optionalFields' => 'Optionele velden', + 'options' => 'Opties', + 'something' => 'Iets!', // budgets: - 'create_new_budget' => 'Maak een nieuw budget', - 'store_new_budget' => 'Sla nieuw budget op', - 'availableIn' => 'Beschikbaar in :date', - 'transactionsWithoutBudget' => 'Uitgaven zonder budget', - 'transactionsWithoutBudgetDate' => 'Uitgaven zonder budget in :date', - 'createBudget' => 'Maak nieuw budget', - 'inactiveBudgets' => 'Inactieve budgetten', - 'without_budget_between' => 'Transacties zonder budget tussen :start en :end', - 'budget_in_month' => ':name in :month', - 'delete_budget' => 'Verwijder budget ":name"', - 'edit_budget' => 'Wijzig budget ":name"', - 'update_amount' => 'Bedrag bijwerken', - 'update_budget' => 'Budget bijwerken', + 'create_new_budget' => 'Maak een nieuw budget', + 'store_new_budget' => 'Sla nieuw budget op', + 'availableIn' => 'Beschikbaar in :date', + 'transactionsWithoutBudget' => 'Uitgaven zonder budget', + 'transactionsWithoutBudgetDate' => 'Uitgaven zonder budget in :date', + 'createBudget' => 'Maak nieuw budget', + 'inactiveBudgets' => 'Inactieve budgetten', + 'without_budget_between' => 'Transacties zonder budget tussen :start en :end', + 'budget_in_month' => ':name in :month', + 'delete_budget' => 'Verwijder budget ":name"', + 'edit_budget' => 'Wijzig budget ":name"', + 'update_amount' => 'Bedrag bijwerken', + 'update_budget' => 'Budget bijwerken', // bills: - 'delete_bill' => 'Verwijder contract ":name"', - 'edit_bill' => 'Wijzig contract ":name"', - 'update_bill' => 'Wijzig contract', - 'store_new_bill' => 'Sla nieuw contract op', + 'delete_bill' => 'Verwijder contract ":name"', + 'edit_bill' => 'Wijzig contract ":name"', + 'update_bill' => 'Wijzig contract', + 'store_new_bill' => 'Sla nieuw contract op', // accounts: - 'details_for_asset' => 'Overzicht voor betaalrekening ":name"', - 'details_for_expense' => 'Overzicht voor crediteur ":name"', - 'details_for_revenue' => 'Overzicht voor debiteur ":name"', - 'details_for_cash' => 'Overzicht voor contant geldrekening ":name"', - 'store_new_asset_account' => 'Sla nieuwe betaalrekening op', - 'store_new_expense_account' => 'Sla nieuwe crediteur op', - 'store_new_revenue_account' => 'Sla nieuwe debiteur op', - 'edit_asset_account' => 'Wijzig betaalrekening ":name"', - 'edit_expense_account' => 'Wijzig crediteur ":name"', - 'edit_revenue_account' => 'Wijzig debiteur ":name"', - 'delete_asset_account' => 'Verwijder betaalrekening ":name"', - 'delete_expense_account' => 'Verwijder crediteur ":name"', - 'delete_revenue_account' => 'Verwijder debiteur ":name"', - 'asset_deleted' => 'Betaalrekening ":name" is verwijderd.', - 'expense_deleted' => 'Crediteur ":name" is verwijderd.', - 'revenue_deleted' => 'Debiteur ":name" is verwijderd.', - 'update_asset_account' => 'Wijzig betaalrekening', - 'update_expense_account' => 'Wijzig crediteur', - 'update_revenue_account' => 'Wijzig debiteur', - 'make_new_asset_account' => 'Nieuwe betaalrekening', - 'make_new_expense_account' => 'Nieuwe crediteur', - 'make_new_revenue_account' => 'Nieuwe debiteur', - 'asset_accounts' => 'Betaalrekeningen', - 'expense_accounts' => 'Crediteuren', - 'revenue_accounts' => 'Debiteuren', - 'accountExtraHelp_asset' => '', - 'accountExtraHelp_expense' => '', - 'accountExtraHelp_revenue' => '', - 'account_type' => 'Rekeningtype', - 'save_transactions_by_moving' => 'Bewaar deze transacties door ze aan een andere rekening te koppelen:', + 'details_for_asset' => 'Overzicht voor betaalrekening ":name"', + 'details_for_expense' => 'Overzicht voor crediteur ":name"', + 'details_for_revenue' => 'Overzicht voor debiteur ":name"', + 'details_for_cash' => 'Overzicht voor contant geldrekening ":name"', + 'store_new_asset_account' => 'Sla nieuwe betaalrekening op', + 'store_new_expense_account' => 'Sla nieuwe crediteur op', + 'store_new_revenue_account' => 'Sla nieuwe debiteur op', + 'edit_asset_account' => 'Wijzig betaalrekening ":name"', + 'edit_expense_account' => 'Wijzig crediteur ":name"', + 'edit_revenue_account' => 'Wijzig debiteur ":name"', + 'delete_asset_account' => 'Verwijder betaalrekening ":name"', + 'delete_expense_account' => 'Verwijder crediteur ":name"', + 'delete_revenue_account' => 'Verwijder debiteur ":name"', + 'asset_deleted' => 'Betaalrekening ":name" is verwijderd.', + 'expense_deleted' => 'Crediteur ":name" is verwijderd.', + 'revenue_deleted' => 'Debiteur ":name" is verwijderd.', + 'update_asset_account' => 'Wijzig betaalrekening', + 'update_expense_account' => 'Wijzig crediteur', + 'update_revenue_account' => 'Wijzig debiteur', + 'make_new_asset_account' => 'Nieuwe betaalrekening', + 'make_new_expense_account' => 'Nieuwe crediteur', + 'make_new_revenue_account' => 'Nieuwe debiteur', + 'asset_accounts' => 'Betaalrekeningen', + 'expense_accounts' => 'Crediteuren', + 'revenue_accounts' => 'Debiteuren', + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => '', + 'accountExtraHelp_revenue' => '', + 'account_type' => 'Rekeningtype', + 'save_transactions_by_moving' => 'Bewaar deze transacties door ze aan een andere rekening te koppelen:', // categories: - 'new_category' => 'Nieuwe categorie', - 'create_new_category' => 'Nieuwe categorie', - 'without_category' => 'Zonder categorie', - 'update_category' => 'Wijzig categorie', - 'categories' => 'Categorieën', - 'edit_category' => 'Wijzig categorie ":name"', - 'no_category' => '(geen categorie)', - 'category' => 'Categorie', - 'delete_category' => 'Verwijder categorie ":name"', - 'store_category' => 'Sla nieuwe categorie op', - 'without_category_between' => 'Zonder categorie tussen :start en :end', + 'new_category' => 'Nieuwe categorie', + 'create_new_category' => 'Nieuwe categorie', + 'without_category' => 'Zonder categorie', + 'update_category' => 'Wijzig categorie', + 'categories' => 'Categorieën', + 'edit_category' => 'Wijzig categorie ":name"', + 'no_category' => '(geen categorie)', + 'category' => 'Categorie', + 'delete_category' => 'Verwijder categorie ":name"', + 'store_category' => 'Sla nieuwe categorie op', + 'without_category_between' => 'Zonder categorie tussen :start en :end', // transactions: - 'update_withdrawal' => 'Wijzig uitgave', - 'update_deposit' => 'Wijzig inkomsten', - 'update_transfer' => 'Wijzig overschrijving', - 'delete_withdrawal' => 'Verwijder uitgave ":description"', - 'delete_deposit' => 'Verwijder inkomsten ":description"', - 'delete_transfer' => 'Verwijder overschrijving ":description"', + 'update_withdrawal' => 'Wijzig uitgave', + 'update_deposit' => 'Wijzig inkomsten', + 'update_transfer' => 'Wijzig overschrijving', + 'delete_withdrawal' => 'Verwijder uitgave ":description"', + 'delete_deposit' => 'Verwijder inkomsten ":description"', + 'delete_transfer' => 'Verwijder overschrijving ":description"', // new user: - 'welcome' => 'Welkom bij Firefly!', - 'createNewAsset' => 'Maak om te beginnen een nieuwe betaalrekening .' . - 'Hiermee kan je nieuwe transacties opslaan en beginnen met het beheren van je geld', - 'createNewAssetButton' => 'Maak een nieuwe betaalrekening', + 'welcome' => 'Welkom bij Firefly!', + 'createNewAsset' => 'Maak om te beginnen een nieuwe betaalrekening .' . + 'Hiermee kan je nieuwe transacties opslaan en beginnen met het beheren van je geld', + 'createNewAssetButton' => 'Maak een nieuwe betaalrekening', // home page: - 'yourAccounts' => 'Je betaalrekeningen', - 'budgetsAndSpending' => 'Budgetten en uitgaven', - 'savings' => 'Sparen', - 'markAsSavingsToContinue' => 'Om hier wat te zien stel je je betaalrekeningen in als "spaarrekening".', - 'createPiggyToContinue' => 'Maak spaarpotjes om hier iets te zien.', - 'newWithdrawal' => 'Nieuwe uitgave', - 'newDeposit' => 'Nieuwe inkomsten', - 'newTransfer' => 'Nieuwe overschrijving', - 'moneyIn' => 'Inkomsten', - 'moneyOut' => 'Uitgaven', - 'billsToPay' => 'Openstaande contracten', - 'billsPaid' => 'Betaalde contracten', - 'viewDetails' => 'Meer info', - 'divided' => 'verdeeld', - 'toDivide' => 'te verdelen', + 'yourAccounts' => 'Je betaalrekeningen', + 'budgetsAndSpending' => 'Budgetten en uitgaven', + 'savings' => 'Sparen', + 'markAsSavingsToContinue' => 'Om hier wat te zien stel je je betaalrekeningen in als "spaarrekening".', + 'createPiggyToContinue' => 'Maak spaarpotjes om hier iets te zien.', + 'newWithdrawal' => 'Nieuwe uitgave', + 'newDeposit' => 'Nieuwe inkomsten', + 'newTransfer' => 'Nieuwe overschrijving', + 'moneyIn' => 'Inkomsten', + 'moneyOut' => 'Uitgaven', + 'billsToPay' => 'Openstaande contracten', + 'billsPaid' => 'Betaalde contracten', + 'viewDetails' => 'Meer info', + 'divided' => 'verdeeld', + 'toDivide' => 'te verdelen', // menu and titles, should be recycled as often as possible: - 'toggleNavigation' => 'Navigatie aan of uit', - 'currency' => 'Valuta', - 'preferences' => 'Voorkeuren', - 'logout' => 'Uitloggen', - 'searchPlaceholder' => 'Zoeken...', - 'dashboard' => 'Dashboard', - 'currencies' => 'Valuta', - 'accounts' => 'Rekeningen', - 'Asset account' => 'Betaalrekening', - 'Default account' => 'Betaalrekening', - 'Expense account' => 'Crediteur', - 'Revenue account' => 'Debiteur', - 'Initial balance account' => 'Startbalansrekening', - 'budgets' => 'Budgetten', - 'tags' => 'Tags', - 'reports' => 'Overzichten', - 'transactions' => 'Transacties', - 'expenses' => 'Uitgaven', - 'income' => 'Inkomsten', - 'transfers' => 'Overschrijvingen', - 'moneyManagement' => 'Geldbeheer', - 'piggyBanks' => 'Spaarpotjes', - 'bills' => 'Contracten', - 'createNew' => 'Nieuw', - 'withdrawal' => 'Uitgave', - 'deposit' => 'Inkomsten', - 'account' => 'Rekening', - 'transfer' => 'Overschrijving', - 'Withdrawal' => 'Uitgave', - 'Deposit' => 'Inkomsten', - 'Transfer' => 'Overschrijving', - 'bill' => 'Contract', - 'yes' => 'Ja', - 'no' => 'Nee', - 'amount' => 'Bedrag', - 'newBalance' => 'Nieuw saldo', - 'overview' => 'Overzicht', - 'saveOnAccount' => 'Sparen op rekening', - 'unknown' => 'Onbekend', - 'daily' => 'Dagelijks', - 'weekly' => 'Wekelijks', - 'monthly' => 'Maandelijks', - 'quarterly' => 'Elk kwartaal', - 'half-year' => 'Elk half jaar', - 'yearly' => 'Jaarlijks', - 'profile' => 'Profiel', + 'toggleNavigation' => 'Navigatie aan of uit', + 'currency' => 'Valuta', + 'preferences' => 'Voorkeuren', + 'logout' => 'Uitloggen', + 'searchPlaceholder' => 'Zoeken...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Valuta', + 'accounts' => 'Rekeningen', + 'Asset account' => 'Betaalrekening', + 'Default account' => 'Betaalrekening', + 'Expense account' => 'Crediteur', + 'Revenue account' => 'Debiteur', + 'Initial balance account' => 'Startbalansrekening', + 'budgets' => 'Budgetten', + 'tags' => 'Tags', + 'reports' => 'Overzichten', + 'transactions' => 'Transacties', + 'expenses' => 'Uitgaven', + 'income' => 'Inkomsten', + 'transfers' => 'Overschrijvingen', + 'moneyManagement' => 'Geldbeheer', + 'piggyBanks' => 'Spaarpotjes', + 'bills' => 'Contracten', + 'createNew' => 'Nieuw', + 'withdrawal' => 'Uitgave', + 'deposit' => 'Inkomsten', + 'account' => 'Rekening', + 'transfer' => 'Overschrijving', + 'Withdrawal' => 'Uitgave', + 'Deposit' => 'Inkomsten', + 'Transfer' => 'Overschrijving', + 'bill' => 'Contract', + 'yes' => 'Ja', + 'no' => 'Nee', + 'amount' => 'Bedrag', + 'newBalance' => 'Nieuw saldo', + 'overview' => 'Overzicht', + 'saveOnAccount' => 'Sparen op rekening', + 'unknown' => 'Onbekend', + 'daily' => 'Dagelijks', + 'weekly' => 'Wekelijks', + 'monthly' => 'Maandelijks', + 'quarterly' => 'Elk kwartaal', + 'half-year' => 'Elk half jaar', + 'yearly' => 'Jaarlijks', + 'profile' => 'Profiel', // reports: - 'report_default' => 'Standaard financieel rapport (:start tot :end)', - 'quick_link_reports' => 'Snelle links', - 'quick_link_default_report' => 'Standaard financieel rapport', - 'report_this_month_quick' => 'Deze maand, alle rekeningen', - 'report_this_year_quick' => 'Dit jaar, alle rekeningen', - 'report_all_time_quick' => 'Gehele periode, alle rekeningen', - 'reports_can_bookmark' => 'Je kan rapporten aan je favorieten toevoegen.', - 'incomeVsExpenses' => 'Inkomsten tegenover uitgaven', - 'accountBalances' => 'Rekeningsaldi', - 'balanceStartOfYear' => 'Saldo aan het begin van het jaar', - 'balanceEndOfYear' => 'Saldo aan het einde van het jaar', - 'balanceStartOfMonth' => 'Saldo aan het begin van de maand', - 'balanceEndOfMonth' => 'Saldo aan het einde van de maand', - 'balanceStart' => 'Saldo aan het begin van de periode', - 'balanceEnd' => 'Saldo aan het einde van de periode', - 'reportsOwnAccounts' => 'Overzichten voor je eigen betaalrekeningen', - 'reportsOwnAccountsAndShared' => 'Overzichten voor je eigen betaalrekeningen en gedeelde rekeningen', - 'splitByAccount' => 'Per betaalrekening', - 'balancedByTransfersAndTags' => 'Gecorrigeerd met overschrijvingen en tags', - 'coveredWithTags' => 'Gecorrigeerd met tags', - 'leftUnbalanced' => 'Ongecorrigeerd', - 'expectedBalance' => 'Verwacht saldo', - 'outsideOfBudgets' => 'Buiten budgetten', - 'leftInBudget' => 'Over van budget', - 'sumOfSums' => 'Alles bij elkaar', - 'noCategory' => '(zonder categorie)', - 'notCharged' => '(Nog) niet betaald', - 'inactive' => 'Niet actief', - 'difference' => 'Verschil', - 'in' => 'In', - 'out' => 'Uit', - 'topX' => 'top :number', - 'showTheRest' => 'Laat alles zien', - 'hideTheRest' => 'Laat alleen de top :number zien', - 'sum_of_year' => 'Som van jaar', - 'sum_of_years' => 'Som van jaren', - 'average_of_year' => 'Gemiddelde in jaar', - 'average_of_years' => 'Gemiddelde in jaren', - 'categories_earned_in_year' => 'Categorieën (inkomsten)', - 'categories_spent_in_year' => 'Categorieën (uitgaven)', - 'report_type' => 'Rapporttype', - 'report_type_default' => 'Standard financieel rapport', - 'report_included_accounts' => 'Accounts in rapport', - 'report_date_range' => 'Datumbereik', - 'report_include_help' => 'Overboekingen naar gedeelde rekeningen tellen als uitgave. Overboekingen van gedeelde rekeningen tellen als inkomsten.', - 'report_preset_ranges' => 'Standaardbereik', - 'shared' => 'Gedeeld', + 'report_default' => 'Standaard financieel rapport (:start tot :end)', + 'quick_link_reports' => 'Snelle links', + 'quick_link_default_report' => 'Standaard financieel rapport', + 'report_this_month_quick' => 'Deze maand, alle rekeningen', + 'report_this_year_quick' => 'Dit jaar, alle rekeningen', + 'report_all_time_quick' => 'Gehele periode, alle rekeningen', + 'reports_can_bookmark' => 'Je kan rapporten aan je favorieten toevoegen.', + 'incomeVsExpenses' => 'Inkomsten tegenover uitgaven', + 'accountBalances' => 'Rekeningsaldi', + 'balanceStartOfYear' => 'Saldo aan het begin van het jaar', + 'balanceEndOfYear' => 'Saldo aan het einde van het jaar', + 'balanceStartOfMonth' => 'Saldo aan het begin van de maand', + 'balanceEndOfMonth' => 'Saldo aan het einde van de maand', + 'balanceStart' => 'Saldo aan het begin van de periode', + 'balanceEnd' => 'Saldo aan het einde van de periode', + 'reportsOwnAccounts' => 'Overzichten voor je eigen betaalrekeningen', + 'reportsOwnAccountsAndShared' => 'Overzichten voor je eigen betaalrekeningen en gedeelde rekeningen', + 'splitByAccount' => 'Per betaalrekening', + 'balancedByTransfersAndTags' => 'Gecorrigeerd met overschrijvingen en tags', + 'coveredWithTags' => 'Gecorrigeerd met tags', + 'leftUnbalanced' => 'Ongecorrigeerd', + 'expectedBalance' => 'Verwacht saldo', + 'outsideOfBudgets' => 'Buiten budgetten', + 'leftInBudget' => 'Over van budget', + 'sumOfSums' => 'Alles bij elkaar', + 'noCategory' => '(zonder categorie)', + 'notCharged' => '(Nog) niet betaald', + 'inactive' => 'Niet actief', + 'difference' => 'Verschil', + 'in' => 'In', + 'out' => 'Uit', + 'topX' => 'top :number', + 'showTheRest' => 'Laat alles zien', + 'hideTheRest' => 'Laat alleen de top :number zien', + 'sum_of_year' => 'Som van jaar', + 'sum_of_years' => 'Som van jaren', + 'average_of_year' => 'Gemiddelde in jaar', + 'average_of_years' => 'Gemiddelde in jaren', + 'categories_earned_in_year' => 'Categorieën (inkomsten)', + 'categories_spent_in_year' => 'Categorieën (uitgaven)', + 'report_type' => 'Rapporttype', + 'report_type_default' => 'Standard financieel rapport', + 'report_included_accounts' => 'Accounts in rapport', + 'report_date_range' => 'Datumbereik', + 'report_include_help' => 'Overboekingen naar gedeelde rekeningen tellen als uitgave. Overboekingen van gedeelde rekeningen tellen als inkomsten.', + 'report_preset_ranges' => 'Standaardbereik', + 'shared' => 'Gedeeld', // charts: - 'dayOfMonth' => 'Dag vd maand', - 'month' => 'Maand', - 'budget' => 'Budget', - 'spent' => 'Uitgegeven', - 'earned' => 'Verdiend', - 'overspent' => 'Teveel uitgegeven', - 'left' => 'Over', - 'noBudget' => '(geen budget)', - 'maxAmount' => 'Maximaal bedrag', - 'minAmount' => 'Minimaal bedrag', - 'billEntry' => 'Bedrag voor dit contract', - 'name' => 'Naam', - 'date' => 'Datum', - 'paid' => 'Betaald', - 'unpaid' => 'Niet betaald', - 'day' => 'Dag', - 'budgeted' => 'Gebudgetteerd', - 'period' => 'Periode', - 'balance' => 'Saldo', - 'summary' => 'Samenvatting', - 'sum' => 'Som', - 'average' => 'Gemiddeld', - 'balanceFor' => 'Saldo op :name', + 'dayOfMonth' => 'Dag vd maand', + 'month' => 'Maand', + 'budget' => 'Budget', + 'spent' => 'Uitgegeven', + 'earned' => 'Verdiend', + 'overspent' => 'Teveel uitgegeven', + 'left' => 'Over', + 'noBudget' => '(geen budget)', + 'maxAmount' => 'Maximaal bedrag', + 'minAmount' => 'Minimaal bedrag', + 'billEntry' => 'Bedrag voor dit contract', + 'name' => 'Naam', + 'date' => 'Datum', + 'paid' => 'Betaald', + 'unpaid' => 'Niet betaald', + 'day' => 'Dag', + 'budgeted' => 'Gebudgetteerd', + 'period' => 'Periode', + 'balance' => 'Saldo', + 'summary' => 'Samenvatting', + 'sum' => 'Som', + 'average' => 'Gemiddeld', + 'balanceFor' => 'Saldo op :name', // piggy banks: - 'piggy_bank' => 'Spaarpotje', - 'new_piggy_bank' => 'Nieuw spaarpotje', - 'store_piggy_bank' => 'Sla spaarpotje op', - 'account_status' => 'Rekeningoverzicht', - 'left_for_piggy_banks' => 'Over voor spaarpotjes', - 'sum_of_piggy_banks' => 'Som van spaarpotjes', - 'saved_so_far' => 'Gespaard', - 'left_to_save' => 'Te sparen', - 'add_money_to_piggy_title' => 'Stop geld in spaarpotje ":name"', - 'remove_money_from_piggy_title' => 'Haal geld uit spaarpotje ":name"', - 'add' => 'Toevoegen', - 'remove' => 'Verwijderen', - 'max_amount_add' => 'Hooguit toe te voegen', - 'max_amount_remove' => 'Hooguit te verwijderen', - 'update_piggy_button' => 'Wijzig spaarpotje', - 'update_piggy_title' => 'Wijzig spaarpotje ":name"', - 'details' => 'Details', - 'events' => 'Gebeurtenissen', - 'target_amount' => 'Doelbedrag', - 'start_date' => 'Startdatum', - 'target_date' => 'Doeldatum', - 'no_target_date' => 'Geen doeldatum', - 'todo' => 'te doen', - 'table' => 'Tabel', - 'piggy_bank_not_exists' => 'Dit spaarpotje bestaat niet meer.', - 'add_any_amount_to_piggy' => 'Stop geld in dit spaarpotje om het doel van :amount te halen.', - 'add_set_amount_to_piggy' => 'Stop voor :date :amount in dit spaarpotje om hem op tijd te vullen.', - 'delete_piggy_bank' => 'Verwijder spaarpotje ":name"', + 'piggy_bank' => 'Spaarpotje', + 'new_piggy_bank' => 'Nieuw spaarpotje', + 'store_piggy_bank' => 'Sla spaarpotje op', + 'account_status' => 'Rekeningoverzicht', + 'left_for_piggy_banks' => 'Over voor spaarpotjes', + 'sum_of_piggy_banks' => 'Som van spaarpotjes', + 'saved_so_far' => 'Gespaard', + 'left_to_save' => 'Te sparen', + 'add_money_to_piggy_title' => 'Stop geld in spaarpotje ":name"', + 'remove_money_from_piggy_title' => 'Haal geld uit spaarpotje ":name"', + 'add' => 'Toevoegen', + 'remove' => 'Verwijderen', + 'max_amount_add' => 'Hooguit toe te voegen', + 'max_amount_remove' => 'Hooguit te verwijderen', + 'update_piggy_button' => 'Wijzig spaarpotje', + 'update_piggy_title' => 'Wijzig spaarpotje ":name"', + 'details' => 'Details', + 'events' => 'Gebeurtenissen', + 'target_amount' => 'Doelbedrag', + 'start_date' => 'Startdatum', + 'target_date' => 'Doeldatum', + 'no_target_date' => 'Geen doeldatum', + 'todo' => 'te doen', + 'table' => 'Tabel', + 'piggy_bank_not_exists' => 'Dit spaarpotje bestaat niet meer.', + 'add_any_amount_to_piggy' => 'Stop geld in dit spaarpotje om het doel van :amount te halen.', + 'add_set_amount_to_piggy' => 'Stop voor :date :amount in dit spaarpotje om hem op tijd te vullen.', + 'delete_piggy_bank' => 'Verwijder spaarpotje ":name"', // tags - 'regular_tag' => 'Een gewone tag.', - 'balancing_act' => 'Er kunnen maar twee transacties worden getagged; een uitgaven en inkomsten. Ze balanceren elkaar.', - 'advance_payment' => 'Je kan een uitgave taggen en zoveel inkomsten om de uitgave (helemaal) te compenseren.', - 'delete_tag' => 'Verwijder tag ":tag"', - 'new_tag' => 'Maak nieuwe tag', - 'edit_tag' => 'Wijzig tag ":tag"', - 'no_year' => 'Zonder jaar', - 'no_month' => 'Zonder maand', - 'tag_title_nothing' => 'Standaard tags', - 'tag_title_balancingAct' => 'Balancerende tags', - 'tag_title_advancePayment' => 'Vooruitbetaalde tags', - 'tags_introduction' => 'Normaal gesproken zijn tags enkele woorden, gebruikt om gerelateerde zaken snel aan elkaar te plakken. dure-aanschaf, rekening, feestje. In Firefly III hebben tags meer betekenis en kan je er een datum, beschrijving en locatie aan geven. Daarmee kan je je transacties op een wat zinvollere manier aan elkaar koppelen. Je kan bijvoorbeeld een tag Kerstdiner maken en informatie over het restaurant meenemen. Zulke tags zijn enkelvoudig; je gebruikt ze maar bij één gelegenheid.', - 'tags_group' => 'Omdat tags transacties groeperen kan je er teruggaves, vergoedingen en andere geldzaken mee aanduiden, zolang de transacties elkaar "opheffen". Hoe je dit aanpakt is aan jou. De gewone manier kan natuurlijk ook.', - 'tags_start' => 'Maak hieronder een tag, of voer nieuwe tags in als je nieuwe transacties maakt.', + 'regular_tag' => 'Een gewone tag.', + 'balancing_act' => 'Er kunnen maar twee transacties worden getagged; een uitgaven en inkomsten. Ze balanceren elkaar.', + 'advance_payment' => 'Je kan een uitgave taggen en zoveel inkomsten om de uitgave (helemaal) te compenseren.', + 'delete_tag' => 'Verwijder tag ":tag"', + 'new_tag' => 'Maak nieuwe tag', + 'edit_tag' => 'Wijzig tag ":tag"', + 'no_year' => 'Zonder jaar', + 'no_month' => 'Zonder maand', + 'tag_title_nothing' => 'Standaard tags', + 'tag_title_balancingAct' => 'Balancerende tags', + 'tag_title_advancePayment' => 'Vooruitbetaalde tags', + 'tags_introduction' => 'Normaal gesproken zijn tags enkele woorden, gebruikt om gerelateerde zaken snel aan elkaar te plakken. dure-aanschaf, rekening, feestje. In Firefly III hebben tags meer betekenis en kan je er een datum, beschrijving en locatie aan geven. Daarmee kan je je transacties op een wat zinvollere manier aan elkaar koppelen. Je kan bijvoorbeeld een tag Kerstdiner maken en informatie over het restaurant meenemen. Zulke tags zijn enkelvoudig; je gebruikt ze maar bij één gelegenheid.', + 'tags_group' => 'Omdat tags transacties groeperen kan je er teruggaves, vergoedingen en andere geldzaken mee aanduiden, zolang de transacties elkaar "opheffen". Hoe je dit aanpakt is aan jou. De gewone manier kan natuurlijk ook.', + 'tags_start' => 'Maak hieronder een tag, of voer nieuwe tags in als je nieuwe transacties maakt.', ]; diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php old mode 100644 new mode 100755 index 5bfb9a1a11..d075a7693f --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -67,6 +67,8 @@ return [ 'filename' => 'Bestandsnaam', 'mime' => 'Bestandstype', 'size' => 'Grootte', + 'trigger' => 'Trigger', + 'stop_processing' => 'Stop met verwerken', 'delete_account' => 'Verwijder rekening ":name"', @@ -76,10 +78,14 @@ return [ 'delete_currency' => 'Verwijder valuta ":name"', 'delete_journal' => 'Verwijder transactie met omschrijving ":description"', 'delete_attachment' => 'Verwijder bijlage ":name"', + 'delete_rule' => 'Verwijder regel ":title"', + 'delete_rule_group' => 'Verwijder regelgroep ":title"', 'attachment_areYouSure' => 'Weet je zeker dat je de bijlage met naam ":name" wilt verwijderen?', 'account_areYouSure' => 'Weet je zeker dat je de rekening met naam ":name" wilt verwijderen?', 'bill_areYouSure' => 'Weet je zeker dat je het contract met naam ":name" wilt verwijderen?', + 'rule_areYouSure' => 'Weet je zeker dat je regel ":title" wilt verwijderen?', + 'ruleGroup_areYouSure' => 'Weet je zeker dat je regelgroep ":title" wilt verwijderen?', 'budget_areYouSure' => 'Weet je zeker dat je het budget met naam ":name" wilt verwijderen?', 'category_areYouSure' => 'Weet je zeker dat je het category met naam ":name" wilt verwijderen?', 'currency_areYouSure' => 'Weet je zeker dat je de valuta met naam ":name" wilt verwijderen?', @@ -89,6 +95,7 @@ return [ 'permDeleteWarning' => 'Dingen verwijderen uit Firefly is permanent en kan niet ongedaan gemaakt worden.', 'also_delete_transactions' => 'Ook de enige transactie verbonden aan deze rekening wordt verwijderd.|Ook alle :count transacties verbonden aan deze rekening worden verwijderd.', + 'also_delete_rules' => 'De enige regel in deze regelgroep wordt ook verwijderd.|Alle :count regels in deze regelgroep worden ook verwijderd.', 'also_delete_piggyBanks' => 'Ook het spaarpotje verbonden aan deze rekening wordt verwijderd.|Ook alle :count spaarpotjes verbonden aan deze rekening worden verwijderd.', 'bill_keep_transactions' => 'De transactie verbonden aan dit contract blijft bewaard.|De :count transacties verbonden aan dit contract blijven bewaard.', 'budget_keep_transactions' => 'De transactie verbonden aan dit budget blijft bewaard.|De :count transacties verbonden aan dit budget blijven bewaard.', diff --git a/resources/lang/nl_NL/help.php b/resources/lang/nl_NL/help.php old mode 100644 new mode 100755 index 7282a1259f..815b05bfc9 --- a/resources/lang/nl_NL/help.php +++ b/resources/lang/nl_NL/help.php @@ -20,74 +20,65 @@ return [ 'main-content-end-text' => 'Elke pagina heeft een vraagtekentje rechtsboven. Gebruik deze voor meer hulp. Veel plezier!', - 'register' => 'register', - 'index' => 'index', - 'home' => 'home', - 'flush' => 'flush', - 'accounts-index' => 'accounts.index', - 'accounts-create' => 'accounts.create', - 'accounts-edit' => 'accounts.edit', - 'accounts-delete' => 'accounts.delete', - 'accounts-show' => 'accounts.show', - 'bills-index' => 'bills.index', - 'bills-rescan' => 'bills.rescan', - 'bills-create' => 'bills.create', - 'bills-edit' => 'bills.edit', - 'bills-delete' => 'bills.delete', - 'bills-show' => 'bills.show', - 'budgets-index' => 'budgets.index', - 'budgets-income' => 'budgets.income', - 'budgets-create' => 'budgets.create', - 'budgets-edit' => 'budgets.edit', - 'budgets-delete' => 'budgets.delete', - 'budgets-show' => 'budgets.show', - 'budgets-noBudget' => 'budgets.noBudget', - 'categories-index' => 'categories.index', - 'categories-create' => 'categories.create', - 'categories-edit' => 'categories.edit', - 'categories-delete' => 'categories.delete', - 'categories-show' => 'categories.show', - 'categories-noCategory' => 'categories.noCategory', - 'csv-index' => 'csv-index', - 'currency-index' => 'currency.index', - 'currency-create' => 'currency.create', - 'currency-edit' => 'currency.edit', - 'currency-delete' => 'currency.delete', - 'currency-default' => 'currency.default', - 'help-show' => 'help.show', - 'json-expense-accounts' => 'json.expense-accounts', - 'json-revenue-accounts' => 'json.revenue-accounts', - 'json-categories' => 'json.categories', - 'json-tags' => 'json.tags', - 'json-box-in' => 'json.box.in', - 'json-box-out' => 'json.box.out', - 'json-box-paid' => 'json.box.paid', - 'json-box-unpaid' => 'json.box.unpaid', - 'new-user-index' => 'new-user.index', - 'piggy-banks-index' => 'piggy-banks.index', - 'piggy-banks-addMoney' => 'piggy-banks.addMoney', - 'piggy-banks-removeMoney' => 'piggy-banks.removeMoney', - 'piggy-banks-create' => 'piggy-banks.create', - 'piggy-banks-edit' => 'piggy-banks.edit', - 'piggy-banks-delete' => 'piggy-banks.delete', - 'piggy-banks-show' => 'piggy-banks.show', - 'preferences' => 'preferences', - 'profile' => 'profile', - 'profile-change-password' => 'profile.change-password', - 'profile-delete-account' => 'profile.delete-account', - 'reports-index' => 'reports.index', - 'reports-year' => 'reports.year', - 'reports-month' => 'reports.month', - 'search' => 'search', - 'tags-index' => 'tags.index', - 'tags-create' => 'tags.create', - 'tags-show' => 'tags.show', - 'tags-edit' => 'tags.edit', - 'tags-delete' => 'tags.delete', - 'transactions-index' => 'transactions.index', - 'transactions-create' => 'transactions.create', - 'transactions-edit' => 'transactions.edit', - 'transactions-delete' => 'transactions.delete', - 'transactions-show' => 'transactions.show', - 'logout' => 'logout', + 'index' => 'index', + 'home' => 'home', + 'accounts-index' => 'rekeningen', + 'accounts-create' => 'maak nieuwe rekening', + 'accounts-edit' => 'rekening wijzigen', + 'accounts-delete' => 'rekening verwijderen', + 'accounts-show' => 'rekening bekijken', + 'attachments-edit' => 'wijzig bijlage', + 'attachments-delete' => 'verwijder bijlage', + 'attachments-show' => 'bekijk bijlage', + 'attachments-preview' => 'bekijik bijlage', + 'bills-index' => 'contracten', + 'bills-create' => 'maak contract', + 'bills-edit' => 'wijzig contract', + 'bills-delete' => 'verwijder contract', + 'bills-show' => 'bekijk contract', + 'budgets-index' => 'budgetten', + 'budgets-create' => 'maak een nieuw budget', + 'budgets-edit' => 'wijzig een budget', + 'budgets-delete' => 'verwijder een budget', + 'budgets-show' => 'bekijk een budget', + 'budgets-noBudget' => 'transacties zonder budget', + 'categories-index' => 'categorieën', + 'categories-create' => 'maak nieuwe categorie', + 'categories-edit' => 'wijzig een categorie', + 'categories-delete' => 'verwijder een categorie', + 'categories-show' => 'bekijk een categorie', + 'categories-show-date' => 'bekijk categorie', + 'categories-noCategory' => 'transacties zonder categorie', + 'csv-index' => 'csv-index', + 'csv-column-roles' => 'kolommen en rollen', + 'csv-map' => 'kolommen en data', + 'csv-download-config-page' => 'download configuratie', + 'csv-process' => 'CSV verwerken', + 'currency-index' => 'valuta\'s', + 'currency-create' => 'maak een nieuwe munteenheid', + 'currency-edit' => 'wijzig een munteenheid', + 'currency-delete' => 'verwijder een munteenheid', + 'new-user-index' => 'nieuwe gebruiker', + 'piggy-banks-index' => 'spaarpotjes', + 'piggy-banks-create' => 'maak nieuw spaarpotje', + 'piggy-banks-edit' => 'wijzig spaarpotje', + 'piggy-banks-delete' => 'verwijder spaarpotje', + 'piggy-banks-show' => 'bekijk spaarportje', + 'preferences' => 'voorkeuren', + 'profile' => 'profiel', + 'profile-change-password' => 'verander je wachtwoord', + 'profile-delete-account' => 'verwijder je account', + 'reports-index' => 'rapporten', + 'reports-report' => 'rapport', + 'search' => 'zoeken', + 'tags-index' => 'tags', + 'tags-create' => 'maak nieuwe tag', + 'tags-show' => 'bekijk tag', + 'tags-edit' => 'wijzig tag', + 'tags-delete' => 'verwijder tag', + 'transactions-index' => 'transacties', + 'transactions-create' => 'maak nieuwe transactie', + 'transactions-edit' => 'wijzig transactie', + 'transactions-delete' => 'verwijder transactie', + 'transactions-show' => 'bekijk transactie', ]; diff --git a/resources/lang/nl_NL/list.php b/resources/lang/nl_NL/list.php old mode 100644 new mode 100755 diff --git a/resources/lang/nl_NL/pagination.php b/resources/lang/nl_NL/pagination.php old mode 100644 new mode 100755 diff --git a/resources/lang/nl_NL/passwords.php b/resources/lang/nl_NL/passwords.php old mode 100644 new mode 100755 diff --git a/resources/lang/nl_NL/validation.php b/resources/lang/nl_NL/validation.php old mode 100644 new mode 100755 index bfd6f6004f..b3da11cde3 --- a/resources/lang/nl_NL/validation.php +++ b/resources/lang/nl_NL/validation.php @@ -1,6 +1,8 @@ 'Deze waarde is niet geldig voor de geselecteerde trigger.', + 'rule_action_value' => 'Deze waarde is niet geldig voor de geselecteerde actie.', 'invalid_domain' => 'Kan niet registereren vanaf dit domein.', 'file_already_attached' => 'Het geuploade bestand ":name" is al gelinkt aan deze transactie.', 'file_attached' => 'Bestand met naam ":name" is met succes geuploaded.', From 8e0e9734a5d328657acfbe468a453def805dc084 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Fri, 15 Jan 2016 15:07:42 +0100 Subject: [PATCH 155/276] Updated gitignore with eclipse files and storage directory --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 24797db2e7..b4883da65a 100755 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,11 @@ Homestead.json _ide_helper.php _ide_helper_models.php .phpstorm.meta.php + +storage/ + +# Eclipse project files +.buildpath +.project +.settings/ + From 61a703e6052f293c907b319b14519f65232664ac Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Fri, 15 Jan 2016 15:24:07 +0100 Subject: [PATCH 156/276] Implemented option to choose field delimiter in CSV import --- app/Helpers/Csv/Data.php | 95 +++++++++++++++++++------- app/Http/Controllers/CsvController.php | 10 ++- resources/lang/en_US/firefly.php | 1 + resources/lang/en_US/form.php | 1 + resources/lang/nl_NL/firefly.php | 1 + resources/lang/nl_NL/form.php | 1 + resources/views/csv/index.twig | 2 + 7 files changed, 87 insertions(+), 24 deletions(-) diff --git a/app/Helpers/Csv/Data.php b/app/Helpers/Csv/Data.php index f77391cb37..f0271091aa 100644 --- a/app/Helpers/Csv/Data.php +++ b/app/Helpers/Csv/Data.php @@ -1,5 +1,4 @@ sessionMapped(); $this->sessionSpecifix(); $this->sessionImportAccount(); + $this->sessionDelimiter(); } protected function sessionHasHeaders() { if (Session::has('csv-has-headers')) { - $this->hasHeaders = (bool)Session::get('csv-has-headers'); + $this->hasHeaders = (bool) Session::get('csv-has-headers'); } } @@ -71,46 +78,54 @@ class Data protected function sessionDateFormat() { if (Session::has('csv-date-format')) { - $this->dateFormat = (string)Session::get('csv-date-format'); + $this->dateFormat = (string) Session::get('csv-date-format'); } } protected function sessionCsvFileLocation() { if (Session::has('csv-file')) { - $this->csvFileLocation = (string)Session::get('csv-file'); + $this->csvFileLocation = (string) Session::get('csv-file'); } } protected function sessionMap() { if (Session::has('csv-map')) { - $this->map = (array)Session::get('csv-map'); + $this->map = (array) Session::get('csv-map'); } } protected function sessionRoles() { if (Session::has('csv-roles')) { - $this->roles = (array)Session::get('csv-roles'); + $this->roles = (array) Session::get('csv-roles'); } } protected function sessionMapped() { if (Session::has('csv-mapped')) { - $this->mapped = (array)Session::get('csv-mapped'); + $this->mapped = (array) Session::get('csv-mapped'); } } protected function sessionSpecifix() { if (Session::has('csv-specifix')) { - $this->specifix = (array)Session::get('csv-specifix'); + $this->specifix = (array) Session::get('csv-specifix'); + } + } + + protected function sessionDelimiter() + { + if (Session::has('csv-delimiter')) { + $this->delimiter = Session::get('csv-delimiter'); } } /** + * * @return string */ public function getDateFormat() @@ -119,7 +134,8 @@ class Data } /** - * @param mixed $dateFormat + * + * @param mixed $dateFormat */ public function setDateFormat($dateFormat) { @@ -128,7 +144,8 @@ class Data } /** - * @param int $importAccount + * + * @param int $importAccount */ public function setImportAccount($importAccount) { @@ -137,6 +154,7 @@ class Data } /** + * * @return bool */ public function hasHeaders() @@ -145,7 +163,8 @@ class Data } /** - * @param bool $hasHeaders + * + * @param bool $hasHeaders */ public function setHasHeaders($hasHeaders) { @@ -154,6 +173,7 @@ class Data } /** + * * @return array */ public function getMap() @@ -162,7 +182,8 @@ class Data } /** - * @param array $map + * + * @param array $map */ public function setMap(array $map) { @@ -171,6 +192,7 @@ class Data } /** + * * @return array */ public function getMapped() @@ -179,7 +201,8 @@ class Data } /** - * @param array $mapped + * + * @param array $mapped */ public function setMapped(array $mapped) { @@ -188,31 +211,33 @@ class Data } /** + * * @return Reader */ public function getReader() { - if (strlen($this->csvFileContent) === 0) { $this->loadCsvFile(); } - + if (is_null($this->reader)) { $this->reader = Reader::createFromString($this->getCsvFileContent()); + $this->reader->setDelimiter($this->delimiter); } - + return $this->reader; } protected function loadCsvFile() { - $file = $this->getCsvFileLocation(); - $content = file_get_contents($file); + $file = $this->getCsvFileLocation(); + $content = file_get_contents($file); $contentDecrypted = Crypt::decrypt($content); $this->setCsvFileContent($contentDecrypted); } /** + * * @return string */ public function getCsvFileLocation() @@ -221,7 +246,8 @@ class Data } /** - * @param string $csvFileLocation + * + * @param string $csvFileLocation */ public function setCsvFileLocation($csvFileLocation) { @@ -230,6 +256,7 @@ class Data } /** + * * @return string */ public function getCsvFileContent() @@ -238,7 +265,8 @@ class Data } /** - * @param string $csvFileContent + * + * @param string $csvFileContent */ public function setCsvFileContent($csvFileContent) { @@ -246,6 +274,7 @@ class Data } /** + * * @return array */ public function getRoles() @@ -254,7 +283,8 @@ class Data } /** - * @param array $roles + * + * @param array $roles */ public function setRoles(array $roles) { @@ -263,6 +293,7 @@ class Data } /** + * * @return array */ public function getSpecifix() @@ -271,7 +302,8 @@ class Data } /** - * @param array $specifix + * + * @param array $specifix */ public function setSpecifix($specifix) { @@ -279,5 +311,22 @@ class Data $this->specifix = $specifix; } + /** + * + * @return string + */ + public function getDelimiter() + { + return $this->delimiter; + } + /** + * + * @param string $delimiter + */ + public function setDelimiter($delimiter) + { + Session::put('csv-delimiter', $delimiter); + $this->delimiter = $delimiter; + } } diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index bde7d5b7e0..5adf0a8bba 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -385,6 +385,13 @@ class CsvController extends Controller $settings['has-headers'] = intval(Input::get('has_headers')) === 1; $settings['specifix'] = Input::get('specifix'); $settings['import-account'] = intval(Input::get('csv_import_account')); + $settings['delimiter'] = Input::get('csv_delimiter', ','); + + // A tab character cannot be used itself as option value in HTML + // See http://stackoverflow.com/questions/6064135/valid-characters-in-option-value + if( $settings[ 'delimiter' ] == 'tab' ) + $settings[ 'delimiter' ] = "\t"; + $settings['map'] = []; $settings['mapped'] = []; $settings['roles'] = []; @@ -405,7 +412,8 @@ class CsvController extends Controller $this->data->setRoles($settings['roles']); $this->data->setSpecifix($settings['specifix']); $this->data->setImportAccount($settings['import-account']); - + $this->data->setDelimiter($settings['delimiter']); + return redirect(route('csv.column-roles')); } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 9beff0f8e6..fa7ef1932c 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -282,6 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', // create new stuff: diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index ec273e435e..5c3f6a4b27 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -53,6 +53,7 @@ return [ 'csv_config' => 'CSV import configuration', 'specifix' => 'Bank- or file specific fixes', 'csv_import_account' => 'Default import account', + 'csv_delimiter' => 'CSV field delimiter', 'attachments[]' => 'Attachments', 'store_new_withdrawal' => 'Store new withdrawal', 'store_new_deposit' => 'Store new deposit', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 7644571945..e42436e6b4 100755 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -282,6 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', + 'csv_delimiter_help' => 'Kies het veld scheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', 'csv_date_parse_error' => 'Firefly kan van ":value" geen datum maken, gegeven het formaat ":format". Weet je zeker dat je CSV goed is?', // create new stuff: diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index d075a7693f..93eb8a75a0 100755 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -53,6 +53,7 @@ return [ 'csv_config' => 'Configuratiebestand', 'specifix' => 'Bank- or of bestandsspecifieke opties', 'csv_import_account' => 'Standaard rekening voor importeren', + 'csv_delimiter' => 'CSV scheidingsteken', 'attachments[]' => 'Bijlagen', 'store_new_withdrawal' => 'Nieuwe uitgave opslaan', 'store_new_deposit' => 'Nieuwe inkomsten opslaan', diff --git a/resources/views/csv/index.twig b/resources/views/csv/index.twig index 4bd88729ce..4e2756dcb0 100644 --- a/resources/views/csv/index.twig +++ b/resources/views/csv/index.twig @@ -63,6 +63,8 @@ {{ ExpandedForm.select('csv_import_account', accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} + {{ ExpandedForm.select('csv_delimiter', { ',': ', (comma)', 'tab': '(tab)', ';': '; (semicolon)'}, 0, {helpText: 'csv_delimiter_help'|_} ) }} + {{ ExpandedForm.multiCheckbox('specifix', specifix) }} From dc9083a764ca759f42066493669a3776126d2c7a Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:26:02 +0100 Subject: [PATCH 157/276] Also forget delimiter. --- app/Http/Controllers/CsvController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index 5adf0a8bba..4347547a1f 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -162,6 +162,7 @@ class CsvController extends Controller Session::forget('csv-roles'); Session::forget('csv-mapped'); Session::forget('csv-specifix'); + SessioN::forget('csv-delimiter'); // get list of supported specifix $specifix = []; From 4f6a7332388d86aa7e07468c48018e49ac9d0148 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:26:19 +0100 Subject: [PATCH 158/276] For more clarity in the code, moved the array of options to the controller itself. --- app/Http/Controllers/CsvController.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index 4347547a1f..1aa19f4361 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -170,6 +170,13 @@ class CsvController extends Controller $specifix[$entry] = trans('firefly.csv_specifix_' . $entry); } + // get a list of delimiters: + $delimiters = [ + ',' => trans('form.csv_comma'), + ';' => trans('form.csv_semicolon'), + 'tab' => trans('form.csv_tab') + ]; + // get a list of asset accounts: $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Asset account', 'Default account'])); From 88839e9610761caa4dce8b7c2b22d648a578cbf1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:26:24 +0100 Subject: [PATCH 159/276] For more clarity in the code, moved the array of options to the controller itself. --- app/Http/Controllers/CsvController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index 1aa19f4361..b1f4cc4605 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -184,7 +184,7 @@ class CsvController extends Controller $uploadPossible = is_writable(storage_path('upload')); $path = storage_path('upload'); - return view('csv.index', compact('subTitle', 'uploadPossible', 'path', 'specifix', 'accounts')); + return view('csv.index', compact('subTitle', 'uploadPossible', 'path', 'specifix', 'accounts', 'delimiters')); } /** From 6c12f1bc86124e32bad9a8393c50f7751d9d9296 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:26:49 +0100 Subject: [PATCH 160/276] Some refactoring, courtesy of PHPStorm. --- app/Http/Controllers/CsvController.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index b1f4cc4605..9d71d394ad 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -394,15 +394,16 @@ class CsvController extends Controller $settings['specifix'] = Input::get('specifix'); $settings['import-account'] = intval(Input::get('csv_import_account')); $settings['delimiter'] = Input::get('csv_delimiter', ','); - + // A tab character cannot be used itself as option value in HTML // See http://stackoverflow.com/questions/6064135/valid-characters-in-option-value - if( $settings[ 'delimiter' ] == 'tab' ) - $settings[ 'delimiter' ] = "\t"; - - $settings['map'] = []; - $settings['mapped'] = []; - $settings['roles'] = []; + if ($settings['delimiter'] == 'tab') { + $settings['delimiter'] = "\t"; + } + + $settings['map'] = []; + $settings['mapped'] = []; + $settings['roles'] = []; if ($request->hasFile('csv_config')) { // Process config file if present. $data = file_get_contents($request->file('csv_config')->getRealPath()); @@ -421,7 +422,7 @@ class CsvController extends Controller $this->data->setSpecifix($settings['specifix']); $this->data->setImportAccount($settings['import-account']); $this->data->setDelimiter($settings['delimiter']); - + return redirect(route('csv.column-roles')); } From ccfc5ece6667816afb4187756475ee50f3070d38 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:27:01 +0100 Subject: [PATCH 161/276] Put the form options in the translation files. --- resources/lang/en_US/form.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 5c3f6a4b27..d1d398ebc2 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -53,7 +53,7 @@ return [ 'csv_config' => 'CSV import configuration', 'specifix' => 'Bank- or file specific fixes', 'csv_import_account' => 'Default import account', - 'csv_delimiter' => 'CSV field delimiter', + 'csv_delimiter' => 'CSV field delimiter', 'attachments[]' => 'Attachments', 'store_new_withdrawal' => 'Store new withdrawal', 'store_new_deposit' => 'Store new deposit', @@ -71,6 +71,10 @@ return [ 'trigger' => 'Trigger', 'stop_processing' => 'Stop processing', + 'csv_comma' => 'A comma (,)', + 'csv_semicolon' => 'A semicolon (;)', + 'csv_tab' => 'A tab (invisible)', + 'delete_account' => 'Delete account ":name"', 'delete_bill' => 'Delete bill ":name"', From 8c55bd179f32b4c12ca86e30cf12d0fbfe8480dc Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:27:17 +0100 Subject: [PATCH 162/276] Moved the option right under the file selection. --- resources/views/csv/index.twig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/views/csv/index.twig b/resources/views/csv/index.twig index 4e2756dcb0..deff9ec3ae 100644 --- a/resources/views/csv/index.twig +++ b/resources/views/csv/index.twig @@ -59,12 +59,13 @@ {{ ExpandedForm.file('csv',{helpText: 'csv_csv_file_help'|_}) }} + {{ ExpandedForm.select('csv_delimiter', delimiters, 0, {helpText: 'csv_delimiter_help'|_} ) }} + {{ ExpandedForm.file('csv_config',{helpText: 'csv_csv_config_file_help'|_}) }} {{ ExpandedForm.select('csv_import_account', accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} - {{ ExpandedForm.select('csv_delimiter', { ',': ', (comma)', 'tab': '(tab)', ';': '; (semicolon)'}, 0, {helpText: 'csv_delimiter_help'|_} ) }} - + {{ ExpandedForm.multiCheckbox('specifix', specifix) }} From 4df1e5393bb7cd58594f5000ead3ed079aa664e7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:33:03 +0100 Subject: [PATCH 163/276] Also updated all translations. --- resources/lang/fr_FR/breadcrumbs.php | 0 resources/lang/fr_FR/config.php | 0 resources/lang/fr_FR/firefly.php | 1001 ++++++++++++++------------ resources/lang/fr_FR/form.php | 39 +- resources/lang/fr_FR/help.php | 141 ++-- resources/lang/fr_FR/list.php | 14 +- resources/lang/fr_FR/pagination.php | 4 +- resources/lang/fr_FR/passwords.php | 11 +- resources/lang/fr_FR/validation.php | 117 ++- resources/lang/nl_NL/firefly.php | 2 +- resources/lang/nl_NL/form.php | 6 +- resources/lang/pt_BR/breadcrumbs.php | 0 resources/lang/pt_BR/config.php | 0 resources/lang/pt_BR/firefly.php | 953 +++++++++++++----------- resources/lang/pt_BR/form.php | 12 + resources/lang/pt_BR/help.php | 131 ++-- resources/lang/pt_BR/list.php | 0 resources/lang/pt_BR/pagination.php | 0 resources/lang/pt_BR/passwords.php | 0 resources/lang/pt_BR/validation.php | 2 + 20 files changed, 1319 insertions(+), 1114 deletions(-) mode change 100644 => 100755 resources/lang/fr_FR/breadcrumbs.php mode change 100644 => 100755 resources/lang/fr_FR/config.php mode change 100644 => 100755 resources/lang/fr_FR/firefly.php mode change 100644 => 100755 resources/lang/fr_FR/form.php mode change 100644 => 100755 resources/lang/fr_FR/help.php mode change 100644 => 100755 resources/lang/fr_FR/list.php mode change 100644 => 100755 resources/lang/fr_FR/pagination.php mode change 100644 => 100755 resources/lang/fr_FR/passwords.php mode change 100644 => 100755 resources/lang/fr_FR/validation.php mode change 100644 => 100755 resources/lang/pt_BR/breadcrumbs.php mode change 100644 => 100755 resources/lang/pt_BR/config.php mode change 100644 => 100755 resources/lang/pt_BR/firefly.php mode change 100644 => 100755 resources/lang/pt_BR/form.php mode change 100644 => 100755 resources/lang/pt_BR/help.php mode change 100644 => 100755 resources/lang/pt_BR/list.php mode change 100644 => 100755 resources/lang/pt_BR/pagination.php mode change 100644 => 100755 resources/lang/pt_BR/passwords.php diff --git a/resources/lang/fr_FR/breadcrumbs.php b/resources/lang/fr_FR/breadcrumbs.php old mode 100644 new mode 100755 diff --git a/resources/lang/fr_FR/config.php b/resources/lang/fr_FR/config.php old mode 100644 new mode 100755 diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php old mode 100644 new mode 100755 index d9dfa8daff..3508e76839 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -1,487 +1,582 @@ 'Vous avez choisi Anglais', - 'close' => 'Fermer', - 'pleaseHold' => 'Veuillew patienter...', - 'actions' => 'Actions', - 'edit' => 'Editer', - 'delete' => 'Supprimer', - 'welcomeBack' => 'What\'s playing?', - 'everything' => 'Everything', - 'customRange' => 'Custom range', - 'apply' => 'Apply', - 'cancel' => 'Cancel', - 'from' => 'From', - 'to' => 'To', - 'total_sum' => 'Total sum', - 'period_sum' => 'Sum for period', - 'showEverything' => 'Show everything', - 'never' => 'Never', - 'search_results_for' => 'Search results for ":query"', - 'bounced_error' => 'The message sent to :email bounced, so no access for you.', - 'deleted_error' => 'These credentials do not match our records.', - 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', - 'removed_amount' => 'Removed :amount', - 'added_amount' => 'Added :amount', - 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', - 'Opening balance' => 'Opening balance', - 'create_new_stuff' => 'Create new stuff', - 'new_withdrawal' => 'New withdrawal', - 'new_deposit' => 'New deposit', - 'new_transfer' => 'New transfer', - 'new_asset_account' => 'New asset account', - 'new_expense_account' => 'New expense account', - 'new_revenue_account' => 'New revenue account', - 'new_budget' => 'New budget', - 'new_bill' => 'New bill', + // general stuff: + 'language_incomplete' => 'Cette langue n\'est pas encore complètement traduite', + 'test' => 'Vous avez choisi Anglais', + 'close' => 'Fermer', + 'pleaseHold' => 'Veuillew patienter...', + 'actions' => 'Actions', + 'edit' => 'Editer', + 'delete' => 'Supprimer', + 'welcomeBack' => 'What\'s playing?', + 'everything' => 'Tout', + 'customRange' => 'Plage personnalisée', + 'apply' => 'Appliquer', + 'cancel' => 'Annuler', + 'from' => 'Depuis', + 'to' => 'To', + 'total_sum' => 'Montant total ', + 'period_sum' => 'Somme pour la période', + 'showEverything' => 'Tout Afficher', + 'never' => 'Jamais', + 'search_results_for' => 'Résultats de recherche pour ":query"', + 'bounced_error' => 'Le message envoyé à :email a été rejeté, donc pas d\'accès pour vous.', + 'deleted_error' => 'These credentials do not match our records.', + 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', + 'removed_amount' => 'Supprimé :amount', + 'added_amount' => 'Ajouté :amount', + 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'Opening balance' => 'Solde initial', + 'create_new_stuff' => 'Create new stuff', + 'new_withdrawal' => 'Nouveau retrait', + 'new_deposit' => 'Nouveau dépôt', + 'new_transfer' => 'Nouveau transfert', + 'new_asset_account' => 'New asset account', + 'new_expense_account' => 'New expense account', + 'new_revenue_account' => 'Nouveau compte de recettes', + 'new_budget' => 'Nouveau budget', + 'new_bill' => 'Nouvelle facture', + + // rules + 'rules' => 'Rules', + 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', + 'update_rule_group' => 'Update rule group', + 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', + 'make_new_rule' => 'Make new rule in rule group ":title"', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', + 'rule_help_active' => 'Inactive rules will never fire.', + 'stored_new_rule' => 'Stored new rule with title ":title"', + 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', + 'updated_rule' => 'Updated rule with title ":title"', + + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'update_rule' => 'Update rule', + + // actions and triggers + 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', + 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', + 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', + + 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', + 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', + 'rule_trigger_from_account_is_choice' => 'Source account is..', + 'rule_trigger_from_account_contains_choice' => 'Source account contains..', + 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', + 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', + 'rule_trigger_to_account_is_choice' => 'Destination account is..', + 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', + 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', + 'rule_trigger_amount_less_choice' => 'Amount is less than..', + 'rule_trigger_amount_exactly_choice' => 'Amount is..', + 'rule_trigger_amount_more_choice' => 'Amount is more than..', + 'rule_trigger_description_starts_choice' => 'Description starts with..', + 'rule_trigger_description_ends_choice' => 'Description ends with..', + 'rule_trigger_description_contains_choice' => 'Description contains..', + 'rule_trigger_description_is_choice' => 'Description is..', + + 'rule_trigger_store_journal' => 'When a journal is created', + 'rule_trigger_update_journal' => 'When a journal is updated', + + 'rule_action_set_category' => 'Set category to ":action_value"', + 'rule_action_clear_category' => 'Clear category', + 'rule_action_set_budget' => 'Set budget to ":action_value"', + 'rule_action_clear_budget' => 'Clear budget', + 'rule_action_add_tag' => 'Add tag ":action_value"', + 'rule_action_remove_tag' => 'Remove tag ":action_value"', + 'rule_action_remove_all_tags' => 'Remove all tags', + 'rule_action_set_description' => 'Set description to ":action_value"', + 'rule_action_append_description' => 'Append description with ":action_value"', + 'rule_action_prepend_description' => 'Prepend description with ":action_value"', + + 'rule_action_set_category_choice' => 'Set category to..', + 'rule_action_clear_category_choice' => 'Clear any category', + 'rule_action_set_budget_choice' => 'Set budget to..', + 'rule_action_clear_budget_choice' => 'Clear any budget', + 'rule_action_add_tag_choice' => 'Add tag..', + 'rule_action_remove_tag_choice' => 'Remove tag..', + 'rule_action_remove_all_tags_choice' => 'Remove all tags', + 'rule_action_set_description_choice' => 'Set description to..', + 'rule_action_append_description_choice' => 'Append description with..', + 'rule_action_prepend_description_choice' => 'Prepend description with..', // tags - 'store_new_tag' => 'Store new tag', - 'update_tag' => 'Update tag', - 'no_location_set' => 'No location set.', - 'meta_data' => 'Meta data', - 'location' => 'Location', + 'store_new_tag' => 'Créer un nouveau tag', + 'update_tag' => 'Mettre à jour le tag', + 'no_location_set' => 'Aucun emplacement défini.', + 'meta_data' => 'Meta-données', + 'location' => 'Emplacement', // preferences - 'pref_home_screen_accounts' => 'Home screen accounts', - 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', - 'pref_budget_settings' => 'Budget settings', - 'pref_budget_settings_help' => 'What\'s the maximum amount of money a budget envelope may contain?', - 'pref_view_range' => 'View range', - 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', - 'pref_1D' => 'One day', - 'pref_1W' => 'One week', - 'pref_1M' => 'One month', - 'pref_3M' => 'Three months (quarter)', - 'pref_6M' => 'Six months', - 'pref_languages' => 'Languages', - 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', - 'pref_save_settings' => 'Save settings', + 'pref_home_screen_accounts' => 'Home screen accounts', + 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', + 'pref_budget_settings' => 'Paramètres de budget', + 'pref_budget_settings_help' => 'What\'s the maximum amount of money a budget envelope may contain?', + 'pref_view_range' => 'View range', + 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', + 'pref_1D' => 'One day', + 'pref_1W' => 'One week', + 'pref_1M' => 'One month', + 'pref_3M' => 'Three months (quarter)', + 'pref_6M' => 'Six months', + 'pref_languages' => 'Languages', + 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', + 'pref_save_settings' => 'Save settings', - // profile - 'change_your_password' => 'Change your password', - 'delete_account' => 'Delete account', - 'current_password' => 'Current password', - 'new_password' => 'New password', - 'new_password_again' => 'New password (again)', - 'delete_your_account' => 'Delete your account', - 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', - 'delete_your_account_password' => 'Enter your password to continue.', - 'password' => 'Password', - 'are_you_sure' => 'Are you sure? You cannot undo this.', - 'delete_account_button' => 'DELETE your account', - 'invalid_current_password' => 'Invalid current password!', - 'password_changed' => 'Password changed!', - 'should_change' => 'The idea is to change your password.', - 'invalid_password' => 'Invalid password!', + // profile: + 'change_your_password' => 'Change your password', + 'delete_account' => 'Delete account', + 'current_password' => 'Current password', + 'new_password' => 'New password', + 'new_password_again' => 'New password (again)', + 'delete_your_account' => 'Delete your account', + 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', + 'delete_your_account_password' => 'Enter your password to continue.', + 'password' => 'Password', + 'are_you_sure' => 'Are you sure? You cannot undo this.', + 'delete_account_button' => 'DELETE your account', + 'invalid_current_password' => 'Invalid current password!', + 'password_changed' => 'Password changed!', + 'should_change' => 'The idea is to change your password.', + 'invalid_password' => 'Invalid password!', // attachments - 'nr_of_attachments' => 'One attachment|:count attachments', - 'attachments' => 'Attachments', - 'edit_attachment' => 'Edit attachment ":name"', - 'update_attachment' => 'Update attachment', - 'delete_attachment' => 'Delete attachment ":name"', - 'attachment_deleted' => 'Deleted attachment ":name"', - 'upload_max_file_size' => 'Maximum file size: :size', + 'nr_of_attachments' => 'One attachment|:count attachments', + 'attachments' => 'Attachments', + 'edit_attachment' => 'Edit attachment ":name"', + 'update_attachment' => 'Update attachment', + 'delete_attachment' => 'Delete attachment ":name"', + 'attachment_deleted' => 'Deleted attachment ":name"', + 'upload_max_file_size' => 'Maximum file size: :size', - // tour - 'prev' => 'Prev', - 'next' => 'Next', - 'end-tour' => 'End tour', - 'pause' => 'Pause', + // tour: + 'prev' => 'Prev', + 'next' => 'Next', + 'end-tour' => 'End tour', + 'pause' => 'Pause', // transaction index - 'title_expenses' => 'Expenses', - 'title_withdrawal' => 'Expenses', - 'title_revenue' => 'Revenue / income', - 'title_deposit' => 'Revenue / income', - 'title_transfer' => 'Transferts', - 'title_transfers' => 'Transferts', + 'title_expenses' => 'Expenses', + 'title_withdrawal' => 'Expenses', + 'title_revenue' => 'Revenue / income', + 'title_deposit' => 'Revenue / income', + 'title_transfer' => 'Transferts', + 'title_transfers' => 'Transferts', - // csv import - 'csv_import' => 'Import CSV file', - 'csv' => 'CSV', - 'csv_index_title' => 'Upload and import a CSV file', - 'csv_define_column_roles' => 'Define column roles', - 'csv_map_values' => 'Map found values to existing values', - 'csv_download_config' => 'Download CSV configuration file.', - 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the button at the top of this page.', - 'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution', - 'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data', - 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: 20151201', - 'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time', - 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'csv_upload_button' => 'Start importing CSV', - 'csv_column_roles_title' => 'Define column roles', - 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.', - 'csv_column_roles_table' => 'Column roles', - 'csv_column' => 'CSV column', - 'csv_column_name' => 'CSV column name', - 'csv_column_example' => 'Column example data', - 'csv_column_role' => 'Column contains?', - 'csv_do_map_value' => 'Map value?', - 'csv_continue' => 'Continue to the next step', - 'csv_go_back' => 'Go back to the previous step', - 'csv_map_title' => 'Map found values to existing values', - 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.', - 'csv_field_value' => 'Field value from CSV', - 'csv_field_mapped_to' => 'Must be mapped to...', - 'csv_do_not_map' => 'Do not map this value', - 'csv_download_config_title' => 'Download CSV configuration', - 'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.', - 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.', - 'csv_do_download_config' => 'Download configuration file.', - 'csv_empty_description' => '(empty description)', - 'csv_upload_form' => 'CSV upload form', - 'csv_index_unsupported_warning' => 'The CSV importer is yet incapable of doing the following:', - 'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.', - 'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".', - 'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.', - 'csv_process_title' => 'CSV import finished!', - 'csv_process_text' => 'The CSV importer has finished and has processed :rows rows', - 'csv_row' => 'Row', - 'csv_import_with_errors' => 'There was one error.|There were :errors errors.', - 'csv_error_see_logs' => 'Check the log files to see details.', - 'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).', - 'csv_start_over' => 'Import again', - 'csv_to_index' => 'Back home', - 'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload', - 'csv_column__ignore' => '(ignore this column)', - 'csv_column_account-iban' => 'Asset account (IBAN)', - 'csv_column_account-id' => 'Asset account ID (matching Firefly)', - 'csv_column_account-name' => 'Asset account (name)', - 'csv_column_amount' => 'Amount', - 'csv_column_bill-id' => 'Bill ID (matching Firefly)', - 'csv_column_bill-name' => 'Bill name', - 'csv_column_budget-id' => 'Budget ID (matching Firefly)', - 'csv_column_budget-name' => 'Budget name', - 'csv_column_category-id' => 'Category ID (matching Firefly)', - 'csv_column_category-name' => 'Category name', - 'csv_column_currency-code' => 'Currency code (ISO 4217)', - 'csv_column_currency-id' => 'Currency ID (matching Firefly)', - 'csv_column_currency-name' => 'Currency name (matching Firefly)', - 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'csv_column_date-rent' => 'Rent calculation date', - 'csv_column_date-transaction' => 'Date', - 'csv_column_description' => 'Description', - 'csv_column_opposing-iban' => 'Opposing account (IBAN)', - 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'csv_column_opposing-name' => 'Opposing account (name)', - 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'csv_column_sepa-db' => 'SEPA Direct Debet', - 'csv_column_tags-comma' => 'Tags (comma separated)', - 'csv_column_tags-space' => 'Tags (space separated)', - 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', - 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', - 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', + // csv import: + 'csv_import' => 'Import CSV file', + 'csv' => 'CSV', + 'csv_index_title' => 'Upload and import a CSV file', + 'csv_define_column_roles' => 'Define column roles', + 'csv_map_values' => 'Map found values to existing values', + 'csv_download_config' => 'Download CSV configuration file.', + 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the button at the top of this page.', + 'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution', + 'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data', + 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time', + 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'csv_upload_button' => 'Start importing CSV', + 'csv_column_roles_title' => 'Define column roles', + 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.', + 'csv_column_roles_table' => 'Column roles', + 'csv_column' => 'CSV column', + 'csv_column_name' => 'CSV column name', + 'csv_column_example' => 'Column example data', + 'csv_column_role' => 'Column contains?', + 'csv_do_map_value' => 'Map value?', + 'csv_continue' => 'Continue to the next step', + 'csv_go_back' => 'Go back to the previous step', + 'csv_map_title' => 'Map found values to existing values', + 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.', + 'csv_field_value' => 'Field value from CSV', + 'csv_field_mapped_to' => 'Must be mapped to...', + 'csv_do_not_map' => 'Do not map this value', + 'csv_download_config_title' => 'Download CSV configuration', + 'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.', + 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.', + 'csv_do_download_config' => 'Download configuration file.', + 'csv_empty_description' => '(empty description)', + 'csv_upload_form' => 'CSV upload form', + 'csv_index_unsupported_warning' => 'The CSV importer is yet incapable of doing the following:', + 'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.', + 'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".', + 'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.', + 'csv_process_title' => 'CSV import finished!', + 'csv_process_text' => 'The CSV importer has finished and has processed :rows rows', + 'csv_row' => 'Row', + 'csv_import_with_errors' => 'There was one error.|There were :errors errors.', + 'csv_error_see_logs' => 'Check the log files to see details.', + 'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).', + 'csv_start_over' => 'Import again', + 'csv_to_index' => 'Back home', + 'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload', + 'csv_column__ignore' => '(ignore this column)', + 'csv_column_account-iban' => 'Asset account (IBAN)', + 'csv_column_account-id' => 'Asset account ID (matching Firefly)', + 'csv_column_account-name' => 'Asset account (name)', + 'csv_column_amount' => 'Amount', + 'csv_column_bill-id' => 'Bill ID (matching Firefly)', + 'csv_column_bill-name' => 'Bill name', + 'csv_column_budget-id' => 'Budget ID (matching Firefly)', + 'csv_column_budget-name' => 'Budget name', + 'csv_column_category-id' => 'Category ID (matching Firefly)', + 'csv_column_category-name' => 'Category name', + 'csv_column_currency-code' => 'Currency code (ISO 4217)', + 'csv_column_currency-id' => 'Currency ID (matching Firefly)', + 'csv_column_currency-name' => 'Currency name (matching Firefly)', + 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'csv_column_date-rent' => 'Rent calculation date', + 'csv_column_date-transaction' => 'Date', + 'csv_column_description' => 'Description', + 'csv_column_opposing-iban' => 'Opposing account (IBAN)', + 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'csv_column_opposing-name' => 'Opposing account (name)', + 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'csv_column_sepa-db' => 'SEPA Direct Debet', + 'csv_column_tags-comma' => 'Tags (comma separated)', + 'csv_column_tags-space' => 'Tags (space separated)', + 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', + 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', + 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', + // create new stuff: + 'create_new_withdrawal' => 'Creer un nouveau retrait', + 'create_new_deposit' => 'Create new deposit', + 'create_new_transfer' => 'Creer un nouveau transfert', + 'create_new_asset' => 'Create new asset account', + 'create_new_expense' => 'Create new expense account', + 'create_new_revenue' => 'Create new revenue account', + 'create_new_piggy_bank' => 'Create new piggy bank', + 'create_new_bill' => 'Create new bill', - // create new stuff - 'create_new_withdrawal' => 'Creer un nouveau retrait', - 'create_new_deposit' => 'Create new deposit', - 'create_new_transfer' => 'Creer un nouveau transfert', - 'create_new_asset' => 'Create new asset account', - 'create_new_expense' => 'Create new expense account', - 'create_new_revenue' => 'Create new revenue account', - 'create_new_piggy_bank' => 'Create new piggy bank', - 'create_new_bill' => 'Create new bill', + // currencies: + 'create_currency' => 'Create a new currency', + 'edit_currency' => 'Edit currency ":name"', + 'store_currency' => 'Store new currency', + 'update_currency' => 'Update currency', + // new user: + 'submit' => 'Submit', + 'getting_started' => 'Getting started', + 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', + 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', + 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', - // currencies - 'create_currency' => 'Create a new currency', - 'edit_currency' => 'Edit currency ":name"', - 'store_currency' => 'Store new currency', - 'update_currency' => 'Update currency', + // forms: + 'mandatoryFields' => 'Mandatory fields', + 'optionalFields' => 'Optional fields', + 'options' => 'Options', + 'something' => 'Something!', - // new user - 'submit' => 'Submit', - 'getting_started' => 'Getting started', - 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', - 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', - 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', + // budgets: + 'create_new_budget' => 'Create a new budget', + 'store_new_budget' => ' Store new budget', + 'availableIn' => 'Available in :date', + 'transactionsWithoutBudget' => 'Expenses without budget', + 'transactionsWithoutBudgetDate' => 'Expenses without budget in :date', + 'createBudget' => 'New budget', + 'inactiveBudgets' => 'Inactive budgets', + 'without_budget_between' => 'Transactions without a budget between :start and :end', + 'budget_in_month' => ':name in :month', + 'delete_budget' => 'Delete budget ":name"', + 'edit_budget' => 'Edit budget ":name"', + 'update_amount' => 'Update amount', + 'update_budget' => 'Update budget', - // forms - 'mandatoryFields' => 'Mandatory fields', - 'optionalFields' => 'Optional fields', - 'options' => 'Options', - 'something' => 'Something!', + // bills: + 'delete_bill' => 'Delete bill ":name"', + 'edit_bill' => 'Edit bill ":name"', + 'update_bill' => 'Update bill', + 'store_new_bill' => 'Store new bill', - // budgets - 'create_new_budget' => 'Create a new budget', - 'store_new_budget' => ' Store new budget', - 'availableIn' => 'Available in :date', - 'transactionsWithoutBudget' => 'Expenses without budget', - 'transactionsWithoutBudgetDate' => 'Expenses without budget in :date', - 'createBudget' => 'New budget', - 'inactiveBudgets' => 'Inactive budgets', - 'without_budget_between' => 'Transactions without a budget between :start and :end', - 'budget_in_month' => ':name in :month', - 'delete_budget' => 'Delete budget ":name"', - 'edit_budget' => 'Edit budget ":name"', - 'update_amount' => 'Update amount', - 'update_budget' => 'Update budget', + // accounts: + 'details_for_asset' => 'Details for asset account ":name"', + 'details_for_expense' => 'Details for expense account ":name"', + 'details_for_revenue' => 'Details for revenue account ":name"', + 'details_for_cash' => 'Details for cash account ":name"', + 'store_new_asset_account' => 'Store new asset account', + 'store_new_expense_account' => 'Store new expense account', + 'store_new_revenue_account' => 'Store new revenue account', + 'edit_asset_account' => 'Edit asset account ":name"', + 'edit_expense_account' => 'Edit expense account ":name"', + 'edit_revenue_account' => 'Edit revenue account ":name"', + 'delete_asset_account' => 'Delete asset account ":name"', + 'delete_expense_account' => 'Delete expense account ":name"', + 'delete_revenue_account' => 'Delete revenue account ":name"', + 'asset_deleted' => 'Successfully deleted asset account ":name"', + 'expense_deleted' => 'Successfully deleted expense account ":name"', + 'revenue_deleted' => 'Successfully deleted revenue account ":name"', + 'update_asset_account' => 'Update asset account', + 'update_expense_account' => 'Update expense account', + 'update_revenue_account' => 'Update revenue account', + 'make_new_asset_account' => 'Create a new asset account', + 'make_new_expense_account' => 'Create a new expense account', + 'make_new_revenue_account' => 'Create a new revenue account', + 'asset_accounts' => 'Asset accounts', + 'expense_accounts' => 'Expense accounts', + 'revenue_accounts' => 'Revenue accounts', + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => '', + 'accountExtraHelp_revenue' => '', + 'account_type' => 'Account type', + 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', - // bills - 'delete_bill' => 'Delete bill ":name"', - 'edit_bill' => 'Edit bill ":name"', - 'update_bill' => 'Update bill', - 'store_new_bill' => 'Store new bill', + // categories: + 'new_category' => 'New category', + 'create_new_category' => 'Create a new category', + 'without_category' => 'Without a category', + 'update_category' => 'Wijzig categorie', + 'categories' => 'Categories', + 'edit_category' => 'Edit category ":name"', + 'no_category' => '(no category)', + 'category' => 'Category', + 'delete_category' => 'Delete category ":name"', + 'store_category' => 'Store new category', + 'without_category_between' => 'Without category between :start and :end', - // accounts - 'details_for_asset' => 'Details for asset account ":name"', - 'details_for_expense' => 'Details for expense account ":name"', - 'details_for_revenue' => 'Details for revenue account ":name"', - 'details_for_cash' => 'Details for cash account ":name"', + // transactions: + 'update_withdrawal' => 'Update withdrawal', + 'update_deposit' => 'Update deposit', + 'update_transfer' => 'Update transfer', + 'delete_withdrawal' => 'Delete withdrawal ":description"', + 'delete_deposit' => 'Delete deposit ":description"', + 'delete_transfer' => 'Delete transfer ":description"', - 'store_new_asset_account' => 'Store new asset account', - 'store_new_expense_account' => 'Store new expense account', - 'store_new_revenue_account' => 'Store new revenue account', + // new user: + 'welcome' => 'Welcome to Firefly!', + 'createNewAsset' => 'Create a new asset account to get started. ' . + 'This will allow you to create transactions and start your financial management', + 'createNewAssetButton' => 'Create new asset account', - 'edit_asset_account' => 'Edit asset account ":name"', - 'edit_expense_account' => 'Edit expense account ":name"', - 'edit_revenue_account' => 'Edit revenue account ":name"', + // home page: + 'yourAccounts' => 'Your accounts', + 'budgetsAndSpending' => 'Budgets and spending', + 'savings' => 'Savings', + 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', + 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', + 'newWithdrawal' => 'New expense', + 'newDeposit' => 'New deposit', + 'newTransfer' => 'New transfer', + 'moneyIn' => 'Money in', + 'moneyOut' => 'Money out', + 'billsToPay' => 'Bills to pay', + 'billsPaid' => 'Bills paid', + 'viewDetails' => 'View details', + 'divided' => 'divided', + 'toDivide' => 'left to divide', - 'delete_asset_account' => 'Delete asset account ":name"', - 'delete_expense_account' => 'Delete expense account ":name"', - 'delete_revenue_account' => 'Delete revenue account ":name"', + // menu and titles, should be recycled as often as possible: + 'toggleNavigation' => 'Toggle navigation', + 'currency' => 'Currency', + 'preferences' => 'Preferences', + 'logout' => 'Logout', + 'searchPlaceholder' => 'Search...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Currencies', + 'accounts' => 'Accounts', + 'Asset account' => 'Asset account', + 'Default account' => 'Asset account', + 'Expense account' => 'Expense account', + 'Revenue account' => 'Revenue account', + 'Initial balance account' => 'Initial balance account', + 'budgets' => 'Budgets', + 'tags' => 'Tags', + 'reports' => 'Reports', + 'transactions' => 'Transactions', + 'expenses' => 'Expenses', + 'income' => 'Revenue / income', + 'transfers' => 'Transferts', + 'moneyManagement' => 'Money management', + 'piggyBanks' => 'Piggy banks', + 'bills' => 'Bills', + 'createNew' => 'Create new', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'account' => 'Account', + 'transfer' => 'Transfer', + 'Withdrawal' => 'Withdrawal', + 'Deposit' => 'Deposit', + 'Transfer' => 'Transfer', + 'bill' => 'Bill', + 'yes' => 'Yes', + 'no' => 'No', + 'amount' => 'Amount', + 'newBalance' => 'New balance', + 'overview' => 'Overview', + 'saveOnAccount' => 'Save on account', + 'unknown' => 'Unknown', + 'daily' => 'Daily', + 'weekly' => 'Weekly', + 'monthly' => 'Monthly', + 'quarterly' => 'Quarterly', + 'half-year' => 'Every six months', + 'yearly' => 'Yearly', + 'profile' => 'Profile', - 'asset_deleted' => 'Successfully deleted asset account ":name"', - 'expense_deleted' => 'Successfully deleted expense account ":name"', - 'revenue_deleted' => 'Successfully deleted revenue account ":name"', + // reports: + 'report_default' => 'Default financial report for :start until :end', + 'quick_link_reports' => 'Quick links', + 'quick_link_default_report' => 'Default financial report', + 'report_this_month_quick' => 'Current month, all accounts', + 'report_this_year_quick' => 'Current year, all accounts', + 'report_all_time_quick' => 'All-time, all accounts', + 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', + 'incomeVsExpenses' => 'Income vs. expenses', + 'accountBalances' => 'Account balances', + 'balanceStartOfYear' => 'Balance at start of year', + 'balanceEndOfYear' => 'Balance at end of year', + 'balanceStartOfMonth' => 'Balance at start of month', + 'balanceEndOfMonth' => 'Balance at end of month', + 'balanceStart' => 'Balance at start of period', + 'balanceEnd' => 'Balance at end of period', + 'reportsOwnAccounts' => 'Reports for your own accounts', + 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'splitByAccount' => 'Split by account', + 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', + 'coveredWithTags' => 'Covered with tags', + 'leftUnbalanced' => 'Left unbalanced', + 'expectedBalance' => 'Expected balance', + 'outsideOfBudgets' => 'Outside of budgets', + 'leftInBudget' => 'Left in budget', + 'sumOfSums' => 'Sum of sums', + 'noCategory' => '(no category)', + 'notCharged' => 'Not charged (yet)', + 'inactive' => 'Inactive', + 'difference' => 'Difference', + 'in' => 'In', + 'out' => 'Out', + 'topX' => 'top :number', + 'showTheRest' => 'Show everything', + 'hideTheRest' => 'Show only the top :number', + 'sum_of_year' => 'Sum of year', + 'sum_of_years' => 'Sum of years', + 'average_of_year' => 'Average of year', + 'average_of_years' => 'Average of years', + 'categories_earned_in_year' => 'Categories (by earnings)', + 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_type' => 'Report type', + 'report_type_default' => 'Default financial report', + 'report_included_accounts' => 'Included accounts', + 'report_date_range' => 'Date range', + 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', + 'report_preset_ranges' => 'Pre-set ranges', + 'shared' => 'Shared', - 'update_asset_account' => 'Update asset account', - 'update_expense_account' => 'Update expense account', - 'update_revenue_account' => 'Update revenue account', + // charts: + 'dayOfMonth' => 'Day of the month', + 'month' => 'Month', + 'budget' => 'Budget', + 'spent' => 'Spent', + 'earned' => 'Earned', + 'overspent' => 'Overspent', + 'left' => 'Left', + 'noBudget' => '(no budget)', + 'maxAmount' => 'Maximum amount', + 'minAmount' => 'Minumum amount', + 'billEntry' => 'Current bill entry', + 'name' => 'Name', + 'date' => 'Date', + 'paid' => 'Paid', + 'unpaid' => 'Unpaid', + 'day' => 'Day', + 'budgeted' => 'Budgeted', + 'period' => 'Period', + 'balance' => 'Balance', + 'summary' => 'Summary', + 'sum' => 'Sum', + 'average' => 'Average', + 'balanceFor' => 'Balance for :name', - 'make_new_asset_account' => 'Create a new asset account', - 'make_new_expense_account' => 'Create a new expense account', - 'make_new_revenue_account' => 'Create a new revenue account', - - 'asset_accounts' => 'Asset accounts', - 'expense_accounts' => 'Expense accounts', - 'revenue_accounts' => 'Revenue accounts', - - 'accountExtraHelp_asset' => '', - 'accountExtraHelp_expense' => '', - 'accountExtraHelp_revenue' => '', - 'account_type' => 'Account type', - 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', - - // categories - 'new_category' => 'New category', - 'create_new_category' => 'Create a new category', - 'without_category' => 'Without a category', - 'update_category' => 'Wijzig categorie', - 'categories' => 'Categories', - 'edit_category' => 'Edit category ":name"', - 'no_category' => '(no category)', - 'category' => 'Category', - 'delete_category' => 'Delete category ":name"', - 'store_category' => 'Store new category', - - // transactions - 'update_withdrawal' => 'Update withdrawal', - 'update_deposit' => 'Update deposit', - 'update_transfer' => 'Update transfer', - 'delete_withdrawal' => 'Delete withdrawal ":description"', - 'delete_deposit' => 'Delete deposit ":description"', - 'delete_transfer' => 'Delete transfer ":description"', - - // new user - 'welcome' => 'Welcome to Firefly!', - 'createNewAsset' => 'Create a new asset account to get started. This will allow you to create transactions and start your financial management', - 'createNewAssetButton' => 'Create new asset account', - - // home page - 'yourAccounts' => 'Your accounts', - 'budgetsAndSpending' => 'Budgets and spending', - 'savings' => 'Savings', - 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', - 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', - 'newWithdrawal' => 'New expense', - 'newDeposit' => 'New deposit', - 'newTransfer' => 'New transfer', - 'moneyIn' => 'Money in', - 'moneyOut' => 'Money out', - 'billsToPay' => 'Bills to pay', - 'billsPaid' => 'Bills paid', - 'viewDetails' => 'View details', - 'divided' => 'divided', - 'toDivide' => 'left to divide', - - // menu and titles, should be recycled as often as possible - 'toggleNavigation' => 'Toggle navigation', - 'currency' => 'Currency', - 'preferences' => 'Preferences', - 'logout' => 'Logout', - 'searchPlaceholder' => 'Search...', - 'dashboard' => 'Dashboard', - 'currencies' => 'Currencies', - 'accounts' => 'Accounts', - 'Asset account' => 'Asset account', - 'Default account' => 'Asset account', - 'Expense account' => 'Expense account', - 'Revenue account' => 'Revenue account', - 'Initial balance account' => 'Initial balance account', - 'budgets' => 'Budgets', - 'tags' => 'Tags', - 'reports' => 'Reports', - 'transactions' => 'Transactions', - 'expenses' => 'Expenses', - 'income' => 'Revenue / income', - 'transfers' => 'Transferts', - 'moneyManagement' => 'Money management', - 'piggyBanks' => 'Piggy banks', - 'bills' => 'Bills', - 'createNew' => 'Create new', - 'withdrawal' => 'Withdrawal', - 'deposit' => 'Deposit', - 'account' => 'Account', - 'transfer' => 'Transfer', - 'Withdrawal' => 'Withdrawal', - 'Deposit' => 'Deposit', - 'Transfer' => 'Transfer', - 'bill' => 'Bill', - 'yes' => 'Yes', - 'no' => 'No', - 'amount' => 'Amount', - 'newBalance' => 'New balance', - 'overview' => 'Overview', - 'saveOnAccount' => 'Save on account', - 'unknown' => 'Unknown', - 'daily' => 'Daily', - 'weekly' => 'Weekly', - 'monthly' => 'Monthly', - 'quarterly' => 'Quarterly', - 'half-year' => 'Every six months', - 'yearly' => 'Yearly', - 'profile' => 'Profile', - - // reports - 'report_default' => 'Default financial report for :start until :end', - 'quick_link_reports' => 'Quick links', - 'quick_link_default_report' => 'Default financial report', - 'report_this_month_quick' => 'Current month, all accounts', - 'report_this_year_quick' => 'Current year, all accounts', - 'report_all_time_quick' => 'All-time, all accounts', - 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', - 'incomeVsExpenses' => 'Income vs. expenses', - 'accountBalances' => 'Account balances', - 'balanceStartOfYear' => 'Balance at start of year', - 'balanceEndOfYear' => 'Balance at end of year', - 'balanceStartOfMonth' => 'Balance at start of month', - 'balanceEndOfMonth' => 'Balance at end of month', - 'balanceStart' => 'Balance at start of period', - 'balanceEnd' => 'Balance at end of period', - 'reportsOwnAccounts' => 'Reports for your own accounts', - 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', - 'splitByAccount' => 'Split by account', - 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', - 'coveredWithTags' => 'Covered with tags', - 'leftUnbalanced' => 'Left unbalanced', - 'expectedBalance' => 'Expected balance', - 'outsideOfBudgets' => 'Outside of budgets', - 'leftInBudget' => 'Left in budget', - 'sumOfSums' => 'Sum of sums', - 'noCategory' => '(no category)', - 'notCharged' => 'Not charged (yet)', - 'inactive' => 'Inactive', - 'difference' => 'Difference', - 'in' => 'In', - 'out' => 'Out', - 'topX' => 'top :number', - 'showTheRest' => 'Show everything', - 'hideTheRest' => 'Show only the top :number', - 'sum_of_year' => 'Sum of year', - 'sum_of_years' => 'Sum of years', - 'average_of_year' => 'Average of year', - 'average_of_years' => 'Average of years', - 'categories_earned_in_year' => 'Categories (by earnings)', - 'categories_spent_in_year' => 'Categories (by spendings)', - 'report_type' => 'Report type', - 'report_type_default' => 'Default financial report', - 'report_included_accounts' => 'Included accounts', - 'report_date_range' => 'Date range', - 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', - 'report_preset_ranges' => 'Pre-set ranges', - 'shared' => 'Shared', - - // charts - 'dayOfMonth' => 'Day of the month', - 'month' => 'Month', - 'budget' => 'Budget', - 'spent' => 'Spent', - 'earned' => 'Earned', - 'overspent' => 'Overspent', - 'left' => 'Left', - 'noBudget' => '(no budget)', - 'maxAmount' => 'Maximum amount', - 'minAmount' => 'Minumum amount', - 'billEntry' => 'Current bill entry', - 'name' => 'Name', - 'date' => 'Date', - 'paid' => 'Paid', - 'unpaid' => 'Unpaid', - 'day' => 'Day', - 'budgeted' => 'Budgeted', - 'period' => 'Period', - 'balance' => 'Balance', - 'summary' => 'Summary', - 'sum' => 'Sum', - 'average' => 'Average', - 'balanceFor' => 'Balance for :name', - - // piggy banks - 'piggy_bank' => 'Piggy bank', - 'new_piggy_bank' => 'Create new piggy bank', - 'store_piggy_bank' => 'Store new piggy bank', - 'account_status' => 'Account status', - 'left_for_piggy_banks' => 'Left for piggy banks', - 'sum_of_piggy_banks' => 'Sum of piggy banks', - 'saved_so_far' => 'Saved so far', - 'left_to_save' => 'Left to save', - 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', - 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', - 'add' => 'Add', - 'remove' => 'Remove', - 'max_amount_add' => 'The maximum amount you can add is', - 'max_amount_remove' => 'The maximum amount you can remove is', - 'update_piggy_button' => 'Update piggy bank', - 'update_piggy_title' => 'Update piggy bank ":name"', - 'details' => 'Details', - 'events' => 'Events', - 'target_amount' => 'Target amount', - 'start_date' => 'Start date', - 'target_date' => 'Target date', - 'no_target_date' => 'No target date', - 'todo' => 'to do', - 'table' => 'Table', - 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', - 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', - 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', - 'delete_piggy_bank' => 'Delete piggy bank ":name"', + // piggy banks: + 'piggy_bank' => 'Piggy bank', + 'new_piggy_bank' => 'Create new piggy bank', + 'store_piggy_bank' => 'Store new piggy bank', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Add', + 'remove' => 'Remove', + 'max_amount_add' => 'The maximum amount you can add is', + 'max_amount_remove' => 'The maximum amount you can remove is', + 'update_piggy_button' => 'Update piggy bank', + 'update_piggy_title' => 'Update piggy bank ":name"', + 'details' => 'Details', + 'events' => 'Events', + 'target_amount' => 'Target amount', + 'start_date' => 'Start date', + 'target_date' => 'Target date', + 'no_target_date' => 'No target date', + 'todo' => 'to do', + 'table' => 'Table', + 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', + 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', + 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', + 'delete_piggy_bank' => 'Delete piggy bank ":name"', // tags - 'regular_tag' => 'Just a regular tag.', - 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', - 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', - - 'delete_tag' => 'Supprimer le tag ":tag"', - 'new_tag' => 'Make new tag', - 'edit_tag' => 'Editer le tag ":tag"', - 'no_year' => 'No year set', - 'no_month' => 'No month set', - 'tag_title_nothing' => 'Default tags', - 'tag_title_balancingAct' => 'Balancing act tags', - 'tag_title_advancePayment' => 'Advance payment tags', - 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', - 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible. ', - 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', + 'regular_tag' => 'Just a regular tag.', + 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', + 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', + 'delete_tag' => 'Supprimer le tag ":tag"', + 'new_tag' => 'Make new tag', + 'edit_tag' => 'Editer le tag ":tag"', + 'no_year' => 'No year set', + 'no_month' => 'No month set', + 'tag_title_nothing' => 'Default tags', + 'tag_title_balancingAct' => 'Balancing act tags', + 'tag_title_advancePayment' => 'Advance payment tags', + 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', + 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', + 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', ]; diff --git a/resources/lang/fr_FR/form.php b/resources/lang/fr_FR/form.php old mode 100644 new mode 100755 index 19b1512659..a572419aec --- a/resources/lang/fr_FR/form.php +++ b/resources/lang/fr_FR/form.php @@ -2,33 +2,33 @@ return [ // new user: - 'bank_name' => 'Bank name', - 'bank_balance' => 'Balance', - 'savings_balance' => 'Savings balance', - 'credit_card_limit' => 'Credit card limit', + 'bank_name' => 'Nom de la banque', + 'bank_balance' => 'Solde', + 'savings_balance' => 'Solde de l\'épargne', + 'credit_card_limit' => 'Limite de carte de crédit', 'automatch' => 'Match automatically', 'skip' => 'Skip', - 'name' => 'Name', - 'active' => 'Active', - 'amount_min' => 'Minimum amount', - 'amount_max' => 'Maximum amount', + 'name' => 'Nom', + 'active' => 'Actif', + 'amount_min' => 'Montant minimum', + 'amount_max' => 'Montant maximum', 'match' => 'Matches on', 'repeat_freq' => 'Repeats', - 'account_from_id' => 'From account', - 'account_to_id' => 'To account', + 'account_from_id' => 'Compte d\'origine', + 'account_to_id' => 'Compte de destination', 'account_id' => 'Asset account', 'budget_id' => 'Budget', - 'openingBalance' => 'Opening balance', + 'openingBalance' => 'Solde initial', 'tagMode' => 'Tag mode', 'tagPosition' => 'Tag location', - 'virtualBalance' => 'Virtual balance', + 'virtualBalance' => 'Solde virtuel', 'longitude_latitude' => 'Location', 'targetamount' => 'Target amount', 'accountRole' => 'Account role', 'openingBalanceDate' => 'Opening balance date', 'ccType' => 'Credit card payment plan', 'ccMonthlyPaymentDate' => 'Credit card monthly payment date', - 'piggy_bank_id' => 'Piggy bank', + 'piggy_bank_id' => 'Tirelire', 'returnHere' => 'Return here', 'returnHereExplanation' => 'After storing, return here to create another one.', 'returnHereUpdateExplanation' => 'After updating, return here.', @@ -53,6 +53,7 @@ return [ 'csv_config' => 'CSV import configuration', 'specifix' => 'Bank- or file specific fixes', 'csv_import_account' => 'Default import account', + 'csv_delimiter' => 'CSV field delimiter', 'attachments[]' => 'Attachments', 'store_new_withdrawal' => 'Store new withdrawal', 'store_new_deposit' => 'Store new deposit', @@ -67,6 +68,13 @@ return [ 'filename' => 'File name', 'mime' => 'Mime type', 'size' => 'Size', + 'trigger' => 'Trigger', + 'stop_processing' => 'Stop processing', + + 'csv_comma' => 'A comma (,)', + 'csv_semicolon' => 'A semicolon (;)', + 'csv_tab' => 'A tab (invisible)', + 'delete_account' => 'Delete account ":name"', 'delete_bill' => 'Supprimer la facture ":name"', @@ -75,10 +83,14 @@ return [ 'delete_currency' => 'Delete currency ":name"', 'delete_journal' => 'Delete transaction with description ":description"', 'delete_attachment' => 'Delete attachment ":name"', + 'delete_rule' => 'Delete rule ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?', 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?', + 'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?', + 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?', 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', @@ -88,6 +100,7 @@ return [ 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', + 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.', 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', diff --git a/resources/lang/fr_FR/help.php b/resources/lang/fr_FR/help.php old mode 100644 new mode 100755 index 75acf0fbdd..71f96fa6f2 --- a/resources/lang/fr_FR/help.php +++ b/resources/lang/fr_FR/help.php @@ -2,7 +2,7 @@ return [ // tour! - 'main-content-title' => 'Welcome to Firefly III', + 'main-content-title' => 'Bienvenue sur Firefly III', 'main-content-text' => 'Do yourself a favor and follow this short guide to make sure you know your way around.', 'sidebar-toggle-title' => 'Sidebar to create stuff', 'sidebar-toggle-text' => 'Hidden under the plus icon are all the buttons to create new stuff. Accounts, transactions, everything!', @@ -13,81 +13,72 @@ return [ 'report-menu-title' => 'Rapport', 'report-menu-text' => 'Check this out when you want a solid overview of your fiances.', 'transaction-menu-title' => 'Transactions', - 'transaction-menu-text' => 'All transactions you\'ve created can be found here.', + 'transaction-menu-text' => 'Toutes les transactions que vous avez créé peuvent être trouvées ici.', 'option-menu-title' => 'Options', - 'option-menu-text' => 'This is pretty self-explanatory.', - 'main-content-end-title' => 'The end!', - 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', + 'option-menu-text' => 'C\'est assez explicite.', + 'main-content-end-title' => 'Fin !', + 'main-content-end-text' => 'N\'oubliez pas que chaque page a un petit point d\'interrogation en haut à droite. Cliquez dessus pour obtenir de l\'aide concernant la page actuelle.', - 'register' => 'register', - 'index' => 'The main index', - 'home' => 'home', - 'flush' => 'flush', - 'accounts-index' => 'accounts.index', - 'accounts-create' => 'accounts.create', - 'accounts-edit' => 'accounts.edit', - 'accounts-delete' => 'accounts.delete', - 'accounts-show' => 'accounts.show', - 'bills-index' => 'bills.index', - 'bills-rescan' => 'bills.rescan', - 'bills-create' => 'bills.create', - 'bills-edit' => 'bills.edit', - 'bills-delete' => 'bills.delete', - 'bills-show' => 'bills.show', - 'budgets-index' => 'budgets.index', - 'budgets-income' => 'budgets.income', - 'budgets-create' => 'budgets.create', - 'budgets-edit' => 'budgets.edit', - 'budgets-delete' => 'budgets.delete', - 'budgets-show' => 'budgets.show', - 'budgets-noBudget' => 'budgets.noBudget', - 'categories-index' => 'categories.index', - 'categories-create' => 'categories.create', - 'categories-edit' => 'categories.edit', - 'categories-delete' => 'categories.delete', - 'categories-show' => 'categories.show', - 'categories-noCategory' => 'categories.noCategory', - 'csv-index' => 'Upload and import a CSV file', - 'currency-index' => 'currency.index', - 'currency-create' => 'currency.create', - 'currency-edit' => 'currency.edit', - 'currency-delete' => 'currency.delete', - 'currency-default' => 'currency.default', - 'help-show' => 'help.show', - 'json-expense-accounts' => 'json.expense-accounts', - 'json-revenue-accounts' => 'json.revenue-accounts', - 'json-categories' => 'json.categories', - 'json-tags' => 'json.tags', - 'json-box-in' => 'json.box.in', - 'json-box-out' => 'json.box.out', - 'json-box-paid' => 'json.box.paid', - 'json-box-unpaid' => 'json.box.unpaid', - 'new-user-index' => 'new-user.index', - 'piggy-banks-index' => 'piggy-banks.index', - 'piggy-banks-addMoney' => 'piggy-banks.addMoney', - 'piggy-banks-removeMoney' => 'piggy-banks.removeMoney', - 'piggy-banks-create' => 'piggy-banks.create', - 'piggy-banks-edit' => 'piggy-banks.edit', - 'piggy-banks-delete' => 'piggy-banks.delete', - 'piggy-banks-show' => 'piggy-banks.show', - 'preferences' => 'preferences', - 'profile' => 'profile', - 'profile-change-password' => 'profile.change-password', - 'profile-delete-account' => 'profile.delete-account', - 'reports-index' => 'reports.index', - 'reports-year' => 'reports.year', - 'reports-month' => 'reports.month', - 'search' => 'search', - 'tags-index' => 'tags.index', - 'tags-create' => 'tags.create', - 'tags-show' => 'tags.show', - 'tags-edit' => 'tags.edit', - 'tags-delete' => 'tags.delete', - 'transactions-index' => 'transactions.index', - 'transactions-create' => 'transactions.create', - 'transactions-edit' => 'transactions.edit', - 'transactions-delete' => 'transactions.delete', - 'transactions-show' => 'transactions.show', - 'logout' => 'logout', + 'index' => 'index', + 'home' => 'home', + 'accounts-index' => 'accounts.index', + 'accounts-create' => 'accounts.create', + 'accounts-edit' => 'accounts.edit', + 'accounts-delete' => 'accounts.delete', + 'accounts-show' => 'accounts.show', + 'attachments-edit' => 'attachments.edit', + 'attachments-delete' => 'attachments.delete', + 'attachments-show' => 'attachments.show', + 'attachments-preview' => 'attachments.preview', + 'bills-index' => 'bills.index', + 'bills-create' => 'bills.create', + 'bills-edit' => 'bills.edit', + 'bills-delete' => 'bills.delete', + 'bills-show' => 'bills.show', + 'budgets-index' => 'budgets.index', + 'budgets-create' => 'budgets.create', + 'budgets-edit' => 'budgets.edit', + 'budgets-delete' => 'budgets.delete', + 'budgets-show' => 'budgets.show', + 'budgets-noBudget' => 'budgets.noBudget', + 'categories-index' => 'categories.index', + 'categories-create' => 'categories.create', + 'categories-edit' => 'categories.edit', + 'categories-delete' => 'categories.delete', + 'categories-show' => 'categories.show', + 'categories-show-date' => 'categories.show.date', + 'categories-noCategory' => 'categories.noCategory', + 'csv-index' => 'csv.index', + 'csv-column-roles' => 'csv.column-roles', + 'csv-map' => 'csv.map', + 'csv-download-config-page' => 'csv.download-config-page', + 'csv-process' => 'csv.process', + 'currency-index' => 'currency.index', + 'currency-create' => 'currency.create', + 'currency-edit' => 'currency.edit', + 'currency-delete' => 'currency.delete', + 'new-user-index' => 'new-user.index', + 'piggy-banks-index' => 'piggy-banks.index', + 'piggy-banks-create' => 'piggy-banks.create', + 'piggy-banks-edit' => 'piggy-banks.edit', + 'piggy-banks-delete' => 'piggy-banks.delete', + 'piggy-banks-show' => 'piggy-banks.show', + 'preferences' => 'preferences', + 'profile' => 'profile', + 'profile-change-password' => 'profile.change-password', + 'profile-delete-account' => 'profile.delete-account', + 'reports-index' => 'reports.index', + 'reports-report' => 'reports.report', + 'search' => 'search', + 'tags-index' => 'tags.index', + 'tags-create' => 'tags.create', + 'tags-show' => 'tags.show', + 'tags-edit' => 'tags.edit', + 'tags-delete' => 'tags.delete', + 'transactions-index' => 'transactions.index', + 'transactions-create' => 'transactions.create', + 'transactions-edit' => 'transactions.edit', + 'transactions-delete' => 'transactions.delete', + 'transactions-show' => 'transactions.show', ]; diff --git a/resources/lang/fr_FR/list.php b/resources/lang/fr_FR/list.php old mode 100644 new mode 100755 index 4bc0b61bc9..9d7fa6e86a --- a/resources/lang/fr_FR/list.php +++ b/resources/lang/fr_FR/list.php @@ -3,15 +3,15 @@ // all table headers. return [ - 'name' => 'Name', - 'role' => 'Role', - 'currentBalance' => 'Current balance', - 'active' => 'Is active?', - 'lastActivity' => 'Last activity', - 'balanceDiff' => 'Balance difference between :start and :end', + 'name' => 'Nom', + 'role' => 'Rôle', + 'currentBalance' => 'Solde courant', + 'active' => 'Actif ?', + 'lastActivity' => 'Activité récente', + 'balanceDiff' => 'Difference solde entre :start et :end', 'matchedOn' => 'Matched on', 'matchesOn' => 'Matched on', - 'matchingAmount' => 'Amount', + 'matchingAmount' => 'Montant', 'lastMatch' => 'Last match', 'expectedMatch' => 'Expected match', 'automatch' => 'Auto match?', diff --git a/resources/lang/fr_FR/pagination.php b/resources/lang/fr_FR/pagination.php old mode 100644 new mode 100755 index fcab34b253..e16f862ea6 --- a/resources/lang/fr_FR/pagination.php +++ b/resources/lang/fr_FR/pagination.php @@ -13,7 +13,7 @@ return [ | */ - 'previous' => '« Previous', - 'next' => 'Next »', + 'previous' => '« Precedent', + 'next' => 'Suivant »', ]; diff --git a/resources/lang/fr_FR/passwords.php b/resources/lang/fr_FR/passwords.php old mode 100644 new mode 100755 index 926c2b197d..81b522829e --- a/resources/lang/fr_FR/passwords.php +++ b/resources/lang/fr_FR/passwords.php @@ -12,10 +12,11 @@ return [ | */ - "password" => "Passwords must be at least six characters and match the confirmation.", - "user" => "We can't find a user with that e-mail address.", - "token" => "This password reset token is invalid.", - "sent" => "We have e-mailed your password reset link!", - "reset" => "Your password has been reset!", + "password" => "Le mot de passe doit contenir au moins 6 caractères et correspondre à la confirmation.", + "user" => "Aucun utilisateur avec cette addresse email.", + "token" => "Le jeton de réinitialisation de mot de passe est invalide.", + "sent" => "Nous avons envoyé votre lien de réinitialisation de mot de passe!", + "reset" => "Le mot de passe a été réinitialisé!", 'blocked' => 'Nice try though.' + ]; diff --git a/resources/lang/fr_FR/validation.php b/resources/lang/fr_FR/validation.php old mode 100644 new mode 100755 index fb45bf34d8..53c07b4811 --- a/resources/lang/fr_FR/validation.php +++ b/resources/lang/fr_FR/validation.php @@ -1,78 +1,69 @@ 'This value is invalid for the selected trigger.', + 'rule_action_value' => 'This value is invalid for the selected action.', 'invalid_domain' => 'Due to security constraints, you cannot register from this domain.', 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', 'file_attached' => 'Succesfully uploaded file ":name".', 'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.', 'file_too_large' => 'File ":name" is too large.', - 'accepted' => 'Le champ :attribute doit être accepté.', + "accepted" => "Le champ :attribute doit être accepté.", + "active_url" => "Le champ :attribute n'est pas une URL valide.", + "after" => "Le champ :attribute doit être une date postérieure au :date.", + "alpha" => "Le champ :attribute doit seulement contenir des lettres.", + "alpha_dash" => "Le champ :attribute doit seulement contenir des lettres, des chiffres et des tirets.", + "alpha_num" => "Le champ :attribute doit seulement contenir des chiffres et des lettres.", + "array" => "Le champ :attribute doit être un tableau.", "unique_for_user" => "There already is an entry with this :attribute.", + "before" => "Le champ :attribute doit être une date antérieure au :date.", 'unique_object_for_user' => 'This name is already in use', 'unique_account_for_user' => 'This account name is already in use', - 'active_url' => "Le champ :attribute n'est pas une URL valide.", - 'after' => 'Le champ :attribute doit être une date postérieure au :date.', - 'alpha' => 'Le champ :attribute doit seulement contenir des lettres.', - 'alpha_dash' => 'Le champ :attribute doit seulement contenir des lettres, des chiffres et des tirets.', - 'alpha_num' => 'Le champ :attribute doit seulement contenir des chiffres et des lettres.', - 'array' => 'Le champ :attribute doit être un tableau.', - 'before' => 'Le champ :attribute doit être une date antérieure au :date.', - 'between.numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.', - 'between.file' => 'Le fichier :attribute doit avoir une taille entre :min et :max kilo-octets.', - 'between.string' => 'Le texte :attribute doit avoir entre :min et :max caractères.', - 'between.array' => 'Le tableau :attribute doit avoir entre :min et :max éléments.', - 'boolean' => 'Le champ :attribute doit être vrai ou faux.', - 'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.', - 'date' => "Le champ :attribute n'est pas une date valide.", - 'date_format' => 'Le champ :attribute ne correspond pas au format :format.', - 'different' => 'Les champs :attribute et :other doivent être différents.', - 'digits' => 'Le champ :attribute doit avoir :digits chiffres.', - 'digits_between' => 'Le champ :attribute doit avoir entre :min et :max chiffres.', - 'email' => 'Le champ :attribute doit être une adresse email valide.', - 'exists' => 'Le champ :attribute sélectionné est invalide.', - 'filled' => 'Le champ :attribute est obligatoire.', - 'image' => 'Le champ :attribute doit être une image.', - 'in' => 'Le champ :attribute est invalide.', - 'integer' => 'Le champ :attribute doit être un entier.', - 'ip' => 'Le champ :attribute doit être une adresse IP valide.', + "between.numeric" => "La valeur de :attribute doit être comprise entre :min et :max.", + "between.file" => "Le fichier :attribute doit avoir une taille entre :min et :max kilo-octets.", + "between.string" => "Le texte :attribute doit avoir entre :min et :max caractères.", + "between.array" => "Le tableau :attribute doit avoir entre :min et :max éléments.", + "boolean" => "Le champ :attribute doit être vrai ou faux.", + "confirmed" => "Le champ de confirmation :attribute ne correspond pas.", + "date" => "Le champ :attribute n'est pas une date valide.", + "date_format" => "Le champ :attribute ne correspond pas au format :format.", + "different" => "Les champs :attribute et :other doivent être différents.", + "digits" => "Le champ :attribute doit avoir :digits chiffres.", + "digits_between" => "Le champ :attribute doit avoir entre :min et :max chiffres.", + "email" => "Le champ :attribute doit être une adresse email valide.", + "filled" => "Le champ :attribute est obligatoire.", + "exists" => "Le champ :attribute sélectionné est invalide.", + "image" => "Le champ :attribute doit être une image.", + "in" => "Le champ :attribute est invalide.", + "integer" => "Le champ :attribute doit être un entier.", + "ip" => "Le champ :attribute doit être une adresse IP valide.", 'json' => 'Le champ :attribute doit être un document JSON valide.', - 'max.numeric' => 'La valeur de :attribute ne peut être supérieure à :max.', - 'max.file' => 'Le fichier :attribute ne peut être plus gros que :max kilo-octets.', - 'max.string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.', - 'max.array' => 'Le tableau :attribute ne peut avoir plus de :max éléments.', - 'mimes' => 'Le champ :attribute doit être un fichier de type : :values.', - 'min.numeric' => 'La valeur de :attribute doit être supérieure à :min.', - 'min.file' => 'Le fichier :attribute doit être plus gros que :min kilo-octets.', - 'min.string' => 'Le texte :attribute doit contenir au moins :min caractères.', - 'min.array' => 'Le tableau :attribute doit avoir au moins :min éléments.', - 'not_in' => "Le champ :attribute sélectionné n'est pas valide.", - 'numeric' => 'Le champ :attribute doit contenir un nombre.', - 'regex' => 'Le format du champ :attribute est invalide.', - 'required' => 'Le champ :attribute est obligatoire.', - 'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.', + "max.numeric" => "La valeur de :attribute ne peut être supérieure à :max.", + "max.file" => "Le fichier :attribute ne peut être plus gros que :max kilo-octets.", + "max.string" => "Le texte de :attribute ne peut contenir plus de :max caractères.", + "max.array" => "Le tableau :attribute ne peut avoir plus de :max éléments.", + "mimes" => "Le champ :attribute doit être un fichier de type : :values.", + "min.numeric" => "La valeur de :attribute doit être supérieure à :min.", + "min.file" => "Le fichier :attribute doit être plus gros que :min kilo-octets.", + "min.string" => "Le texte :attribute doit contenir au moins :min caractères.", + "min.array" => "Le tableau :attribute doit avoir au moins :min éléments.", + "not_in" => "Le champ :attribute sélectionné n'est pas valide.", + "numeric" => "Le champ :attribute doit contenir un nombre.", + "regex" => "Le format du champ :attribute est invalide.", + "required" => "Le champ :attribute est obligatoire.", + "required_if" => "Le champ :attribute est obligatoire quand la valeur de :other est :value.", 'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.', - 'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.', - 'required_with_all' => 'Le champ :attribute est obligatoire quand :values est présent.', - 'required_without' => "Le champ :attribute est obligatoire quand :values n'est pas présent.", - 'required_without_all' => "Le champ :attribute est requis quand aucun de :values n'est présent.", - 'same' => 'Les champs :attribute et :other doivent être identiques.', - 'size.numeric' => 'La valeur de :attribute doit être :size.', - 'size.file' => 'La taille du fichier de :attribute doit être de :size kilo-octets.', - 'size.string' => 'Le texte de :attribute doit contenir :size caractères.', - 'size.array' => 'Le tableau :attribute doit contenir :size éléments.', + "required_with" => "Le champ :attribute est obligatoire quand :values est présent.", + "required_with_all" => "Le champ :attribute est obligatoire quand :values est présent.", + "required_without" => "Le champ :attribute est obligatoire quand :values n'est pas présent.", + "required_without_all" => "Le champ :attribute est requis quand aucun de :values n'est présent.", + "same" => "Les champs :attribute et :other doivent être identiques.", + "size.numeric" => "La valeur de :attribute doit être :size.", + "size.file" => "La taille du fichier de :attribute doit être de :size kilo-octets.", + "size.string" => "Le texte de :attribute doit contenir :size caractères.", + "size.array" => "Le tableau :attribute doit contenir :size éléments.", + "unique" => "La valeur du champ :attribute est déjà utilisée.", 'string' => 'Le champ :attribute doit être une chaîne de caractères.', - 'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.', - 'unique' => 'La valeur du champ :attribute est déjà utilisée.', - 'url' => "Le format de l'URL de :attribute n'est pas valide.", + "url" => "Le format de l'URL de :attribute n'est pas valide.", + "timezone" => "Le champ :attribute doit être un fuseau horaire valide.", ]; diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index e42436e6b4..73fe1865c3 100755 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -282,7 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', - 'csv_delimiter_help' => 'Kies het veld scheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', + 'csv_delimiter_help' => 'Kies het veldscheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', 'csv_date_parse_error' => 'Firefly kan van ":value" geen datum maken, gegeven het formaat ":format". Weet je zeker dat je CSV goed is?', // create new stuff: diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index 93eb8a75a0..cce6489790 100755 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -53,7 +53,7 @@ return [ 'csv_config' => 'Configuratiebestand', 'specifix' => 'Bank- or of bestandsspecifieke opties', 'csv_import_account' => 'Standaard rekening voor importeren', - 'csv_delimiter' => 'CSV scheidingsteken', + 'csv_delimiter' => 'CSV scheidingsteken', 'attachments[]' => 'Bijlagen', 'store_new_withdrawal' => 'Nieuwe uitgave opslaan', 'store_new_deposit' => 'Nieuwe inkomsten opslaan', @@ -71,6 +71,10 @@ return [ 'trigger' => 'Trigger', 'stop_processing' => 'Stop met verwerken', + 'csv_comma' => 'Een komma (,)', + 'csv_semicolon' => 'Een puntkomma (;)', + 'csv_tab' => 'Een tab (onzichtbaar)', + 'delete_account' => 'Verwijder rekening ":name"', 'delete_bill' => 'Verwijder contract ":name"', diff --git a/resources/lang/pt_BR/breadcrumbs.php b/resources/lang/pt_BR/breadcrumbs.php old mode 100644 new mode 100755 diff --git a/resources/lang/pt_BR/config.php b/resources/lang/pt_BR/config.php old mode 100644 new mode 100755 diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php old mode 100644 new mode 100755 index 23584f877e..0ca7a858b5 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -2,476 +2,581 @@ return [ // general stuff: - 'test' => 'Você selecionou Inglês', - 'close' => 'Fechar', - 'pleaseHold' => 'Por favor espere...', - 'actions' => 'Ações', - 'edit' => 'Editar', - 'delete' => 'Apagar', - 'welcomeBack' => 'What\'s playing?', - 'everything' => 'Tudo', - 'customRange' => 'Intervalo Personalizado', - 'apply' => 'Aplicar', - 'cancel' => 'Cancelar', - 'from' => 'De', - 'to' => 'Até', - 'total_sum' => 'Soma Total', - 'period_sum' => 'Soma por período', - 'showEverything' => 'Mostrar tudo', - 'never' => 'Never', - 'search_results_for' => 'Pesquisar resultados por ":query"', - 'bounced_error' => 'A mensagem enviado para :email ressaltado, não tem acesso para você.', - 'deleted_error' => 'Estas credenciais não correspondem aos nossos registros.', - 'general_blocked_error' => 'Sua conta foi desativada, você não pode entrar.', - 'removed_amount' => ':amount removido', - 'added_amount' => ':amount adicionada', - 'asset_account_role_help' => 'Quaisquer opções extras resultantes da sua escolha pode ser definido mais tarde.', - 'Opening balance' => 'Saldo inicial', - 'create_new_stuff' => 'Criar novas coisas', - 'new_withdrawal' => 'Nova retirada', - 'new_deposit' => 'Novo depósito', - 'new_transfer' => 'Nova transferência', - 'new_asset_account' => 'Nova conta de ativo', - 'new_expense_account' => 'Nova conta de despesa', - 'new_revenue_account' => 'Nova conta de receita', - 'new_budget' => 'Novo orçamento', - 'new_bill' => 'Nova fatura', + 'language_incomplete' => 'This language is not yet fully translated', + 'test' => 'Você selecionou Inglês', + 'close' => 'Fechar', + 'pleaseHold' => 'Por favor espere...', + 'actions' => 'Ações', + 'edit' => 'Editar', + 'delete' => 'Apagar', + 'welcomeBack' => 'What\'s playing?', + 'everything' => 'Tudo', + 'customRange' => 'Intervalo Personalizado', + 'apply' => 'Aplicar', + 'cancel' => 'Cancelar', + 'from' => 'De', + 'to' => 'Até', + 'total_sum' => 'Soma Total', + 'period_sum' => 'Soma por período', + 'showEverything' => 'Mostrar tudo', + 'never' => 'Never', + 'search_results_for' => 'Pesquisar resultados por ":query"', + 'bounced_error' => 'A mensagem enviado para :email ressaltado, não tem acesso para você.', + 'deleted_error' => 'Estas credenciais não correspondem aos nossos registros.', + 'general_blocked_error' => 'Sua conta foi desativada, você não pode entrar.', + 'removed_amount' => ':amount removido', + 'added_amount' => ':amount adicionada', + 'asset_account_role_help' => 'Quaisquer opções extras resultantes da sua escolha pode ser definido mais tarde.', + 'Opening balance' => 'Saldo inicial', + 'create_new_stuff' => 'Criar novas coisas', + 'new_withdrawal' => 'Nova retirada', + 'new_deposit' => 'Novo depósito', + 'new_transfer' => 'Nova transferência', + 'new_asset_account' => 'Nova conta de ativo', + 'new_expense_account' => 'Nova conta de despesa', + 'new_revenue_account' => 'Nova conta de receita', + 'new_budget' => 'Novo orçamento', + 'new_bill' => 'Nova fatura', + + // rules + 'rules' => 'Rules', + 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', + 'update_rule_group' => 'Update rule group', + 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', + 'make_new_rule' => 'Make new rule in rule group ":title"', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', + 'rule_help_active' => 'Inactive rules will never fire.', + 'stored_new_rule' => 'Stored new rule with title ":title"', + 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', + 'updated_rule' => 'Updated rule with title ":title"', + + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'update_rule' => 'Update rule', + + // actions and triggers + 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', + 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', + 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', + + 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', + 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', + 'rule_trigger_from_account_is_choice' => 'Source account is..', + 'rule_trigger_from_account_contains_choice' => 'Source account contains..', + 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', + 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', + 'rule_trigger_to_account_is_choice' => 'Destination account is..', + 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', + 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', + 'rule_trigger_amount_less_choice' => 'Amount is less than..', + 'rule_trigger_amount_exactly_choice' => 'Amount is..', + 'rule_trigger_amount_more_choice' => 'Amount is more than..', + 'rule_trigger_description_starts_choice' => 'Description starts with..', + 'rule_trigger_description_ends_choice' => 'Description ends with..', + 'rule_trigger_description_contains_choice' => 'Description contains..', + 'rule_trigger_description_is_choice' => 'Description is..', + + 'rule_trigger_store_journal' => 'When a journal is created', + 'rule_trigger_update_journal' => 'When a journal is updated', + + 'rule_action_set_category' => 'Set category to ":action_value"', + 'rule_action_clear_category' => 'Clear category', + 'rule_action_set_budget' => 'Set budget to ":action_value"', + 'rule_action_clear_budget' => 'Clear budget', + 'rule_action_add_tag' => 'Add tag ":action_value"', + 'rule_action_remove_tag' => 'Remove tag ":action_value"', + 'rule_action_remove_all_tags' => 'Remove all tags', + 'rule_action_set_description' => 'Set description to ":action_value"', + 'rule_action_append_description' => 'Append description with ":action_value"', + 'rule_action_prepend_description' => 'Prepend description with ":action_value"', + + 'rule_action_set_category_choice' => 'Set category to..', + 'rule_action_clear_category_choice' => 'Clear any category', + 'rule_action_set_budget_choice' => 'Set budget to..', + 'rule_action_clear_budget_choice' => 'Clear any budget', + 'rule_action_add_tag_choice' => 'Add tag..', + 'rule_action_remove_tag_choice' => 'Remove tag..', + 'rule_action_remove_all_tags_choice' => 'Remove all tags', + 'rule_action_set_description_choice' => 'Set description to..', + 'rule_action_append_description_choice' => 'Append description with..', + 'rule_action_prepend_description_choice' => 'Prepend description with..', // tags - 'store_new_tag' => 'Armazenar nova tag', - 'update_tag' => 'Atualizar tag', - 'no_location_set' => 'Nenhuma localização.', - 'meta_data' => 'Meta dados', - 'location' => 'Localização', + 'store_new_tag' => 'Armazenar nova tag', + 'update_tag' => 'Atualizar tag', + 'no_location_set' => 'Nenhuma localização.', + 'meta_data' => 'Meta dados', + 'location' => 'Localização', // preferences - 'pref_home_screen_accounts' => 'Conta da tela inicial', - 'pref_home_screen_accounts_help' => 'Que conta deve ser exibida na tela inicial?', - 'pref_budget_settings' => 'Definições de Orçamento', - 'pref_budget_settings_help' => 'Qual a quantidade máxima de dinheiro um envelope orçamental pode conter?', - 'pref_view_range' => 'Ver intervalo', - 'pref_view_range_help' => 'Alguns gráficos são agrupados automaticamente em períodos. Qual período você prefere?', - 'pref_1D' => 'Um dia', - 'pref_1W' => 'Uma semana', - 'pref_1M' => 'Um mês', - 'pref_3M' => 'Trimestral', - 'pref_6M' => 'Semestral', - 'pref_languages' => 'Idiomas', - 'pref_languages_help' => 'Firefly III suporta muitos idiomas. Qual você prefere?', - 'pref_save_settings' => 'Salvar definições', + 'pref_home_screen_accounts' => 'Conta da tela inicial', + 'pref_home_screen_accounts_help' => 'Que conta deve ser exibida na tela inicial?', + 'pref_budget_settings' => 'Definições de Orçamento', + 'pref_budget_settings_help' => 'Qual a quantidade máxima de dinheiro um envelope orçamental pode conter?', + 'pref_view_range' => 'Ver intervalo', + 'pref_view_range_help' => 'Alguns gráficos são agrupados automaticamente em períodos. Qual período você prefere?', + 'pref_1D' => 'Um dia', + 'pref_1W' => 'Uma semana', + 'pref_1M' => 'Um mês', + 'pref_3M' => 'Trimestral', + 'pref_6M' => 'Semestral', + 'pref_languages' => 'Idiomas', + 'pref_languages_help' => 'Firefly III suporta muitos idiomas. Qual você prefere?', + 'pref_save_settings' => 'Salvar definições', // profile: - 'change_your_password' => 'Alterar sua senha', - 'delete_account' => 'Apagar conta', - 'current_password' => 'Senha atual', - 'new_password' => 'Nova senha', - 'new_password_again' => 'Nova senha (novamente)', - 'delete_your_account' => 'Apagar sua conta', - 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', - 'delete_your_account_password' => 'Coloque sua senha para continuar.', - 'password' => 'Senha', - 'are_you_sure' => 'Você tem certeza? Você não poderá desfazer isso.', - 'delete_account_button' => 'Apagar sua conta', - 'invalid_current_password' => 'Senha atual inválida!', - 'password_changed' => 'Senha alterada!', - 'should_change' => 'A idéia é alterar sua senha.', - 'invalid_password' => 'Senha inválida!', + 'change_your_password' => 'Alterar sua senha', + 'delete_account' => 'Apagar conta', + 'current_password' => 'Senha atual', + 'new_password' => 'Nova senha', + 'new_password_again' => 'Nova senha (novamente)', + 'delete_your_account' => 'Apagar sua conta', + 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', + 'delete_your_account_password' => 'Coloque sua senha para continuar.', + 'password' => 'Senha', + 'are_you_sure' => 'Você tem certeza? Você não poderá desfazer isso.', + 'delete_account_button' => 'Apagar sua conta', + 'invalid_current_password' => 'Senha atual inválida!', + 'password_changed' => 'Senha alterada!', + 'should_change' => 'A idéia é alterar sua senha.', + 'invalid_password' => 'Senha inválida!', // attachments - 'nr_of_attachments' => 'Um anexo|:count anexos', - 'attachments' => 'Anexos', - 'edit_attachment' => 'Editar anexo ":name"', - 'update_attachment' => 'Atualizar anexo', - 'delete_attachment' => 'Apagar anexo ":name"', - 'attachment_deleted' => 'Anexo apagado ":name"', - 'upload_max_file_size' => 'Tamanho máximo do arquivo: :size', + 'nr_of_attachments' => 'Um anexo|:count anexos', + 'attachments' => 'Anexos', + 'edit_attachment' => 'Editar anexo ":name"', + 'update_attachment' => 'Atualizar anexo', + 'delete_attachment' => 'Apagar anexo ":name"', + 'attachment_deleted' => 'Anexo apagado ":name"', + 'upload_max_file_size' => 'Tamanho máximo do arquivo: :size', // tour: - 'prev' => 'Anterior', - 'next' => 'Próximo', - 'end-tour' => 'Fim do Tour', - 'pause' => 'Parar', + 'prev' => 'Anterior', + 'next' => 'Próximo', + 'end-tour' => 'Fim do Tour', + 'pause' => 'Parar', // transaction index - 'title_expenses' => 'Despesas', - 'title_withdrawal' => 'Despesas', - 'title_revenue' => 'Receitas / Renda', - 'title_deposit' => 'Receita / Renda', - 'title_transfer' => 'Transferências', - 'title_transfers' => 'Transferências', + 'title_expenses' => 'Despesas', + 'title_withdrawal' => 'Despesas', + 'title_revenue' => 'Receitas / Renda', + 'title_deposit' => 'Receita / Renda', + 'title_transfer' => 'Transferências', + 'title_transfers' => 'Transferências', // csv import: - 'csv_import' => 'Importar arquivo CSV', - 'csv' => 'CSV', - 'csv_index_title' => 'Carregar e importar um arquivo CSV', - 'csv_define_column_roles' => 'Definir papeis da coluna', - 'csv_map_values' => 'Valores mapeados encontrados para valores existentes', - 'csv_download_config' => 'Download do arquivo CSV de configuração.', - 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the button at the top of this page.', - 'csv_index_beta_warning' => 'Esta ferramenta está em beta. Por favor proceder com cautela', - 'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data', - 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time', - 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'csv_upload_button' => 'Iniciando importação do CSV', - 'csv_column_roles_title' => 'Definir papeis da coluna', - 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.', - 'csv_column_roles_table' => 'Papéis da Coluna', - 'csv_column' => 'Coluna CSV', - 'csv_column_name' => 'Nome da coluna do CSV', - 'csv_column_example' => 'Exemplo de dados da coluna', - 'csv_column_role' => 'Coluna contém?', - 'csv_do_map_value' => 'Valor mapeado?', - 'csv_continue' => 'Continuar para o próximo passo', - 'csv_go_back' => 'Voltar para o passo anterior', - 'csv_map_title' => 'Valores mapeados encontrados para valores existentes', - 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.', - 'csv_field_value' => 'Valor do campo do CSV', - 'csv_field_mapped_to' => 'Deve ser mapeado para...', - 'csv_do_not_map' => 'Não mapear este valor', - 'csv_download_config_title' => 'Download do CSV de configuração ', - 'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.', - 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.', - 'csv_do_download_config' => 'Download do arquivo de configuração.', - 'csv_empty_description' => '(descrição vazia)', - 'csv_upload_form' => 'Formulário de Upload do CSV', - 'csv_index_unsupported_warning' => 'O importador de CSV está incapaz de fazer o seguinte:', - 'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.', - 'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".', - 'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.', - 'csv_process_title' => 'Importação do CSV terminou!', - 'csv_process_text' => 'O importador do CSV terminou e processou :rows linhas', - 'csv_row' => 'Linha', - 'csv_import_with_errors' => 'Houve um erro.|Houve :errors erros.', - 'csv_error_see_logs' => 'Verifique o arquivo de log para ver detalhes.', - 'csv_process_new_entries' => 'Firefly criou :imported nova(s) transação(ões)', - 'csv_start_over' => 'Importar novamente', - 'csv_to_index' => 'Voltar para tela inicial', - 'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload', - 'csv_column__ignore' => '(ignorar esta coluna)', - 'csv_column_account-iban' => 'Conta de Ativo (IBAN)', - 'csv_column_account-id' => 'ID da Conta de Ativo (correspondente Firefly)', - 'csv_column_account-name' => 'Conta de Ativo (nome)', - 'csv_column_amount' => 'Valor', - 'csv_column_bill-id' => 'ID Fatura (correspondente Firefly)', - 'csv_column_bill-name' => 'Nom da Fatura', - 'csv_column_budget-id' => 'ID do Orçamento (correspondente Firefly)', - 'csv_column_budget-name' => 'Nome do Orçamento', - 'csv_column_category-id' => 'ID da Categoria (correspondente Firefly)', - 'csv_column_category-name' => 'Nome da Categoria', - 'csv_column_currency-code' => 'Código da Moeda (ISO 4217)', - 'csv_column_currency-id' => 'ID da Moeda (correspondente Firefly)', - 'csv_column_currency-name' => 'Nome da Moeda (correspondente Firefly)', - 'csv_column_currency-symbol' => 'Símbolo da Moeda (correspondente Firefly)', - 'csv_column_date-rent' => 'Rent calculation date', - 'csv_column_date-transaction' => 'Data', - 'csv_column_description' => 'Descrição', - 'csv_column_opposing-iban' => 'Opposing account (IBAN)', - 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'csv_column_opposing-name' => 'Opposing account (name)', - 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'csv_column_sepa-db' => 'SEPA Direct Debet', - 'csv_column_tags-comma' => 'Tags (separadas por vírgula)', - 'csv_column_tags-space' => 'Tags (separadas por espaço)', - 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', - 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', - 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', + 'csv_import' => 'Importar arquivo CSV', + 'csv' => 'CSV', + 'csv_index_title' => 'Carregar e importar um arquivo CSV', + 'csv_define_column_roles' => 'Definir papeis da coluna', + 'csv_map_values' => 'Valores mapeados encontrados para valores existentes', + 'csv_download_config' => 'Download do arquivo CSV de configuração.', + 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the button at the top of this page.', + 'csv_index_beta_warning' => 'Esta ferramenta está em beta. Por favor proceder com cautela', + 'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data', + 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time', + 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'csv_upload_button' => 'Iniciando importação do CSV', + 'csv_column_roles_title' => 'Definir papeis da coluna', + 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.', + 'csv_column_roles_table' => 'Papéis da Coluna', + 'csv_column' => 'Coluna CSV', + 'csv_column_name' => 'Nome da coluna do CSV', + 'csv_column_example' => 'Exemplo de dados da coluna', + 'csv_column_role' => 'Coluna contém?', + 'csv_do_map_value' => 'Valor mapeado?', + 'csv_continue' => 'Continuar para o próximo passo', + 'csv_go_back' => 'Voltar para o passo anterior', + 'csv_map_title' => 'Valores mapeados encontrados para valores existentes', + 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.', + 'csv_field_value' => 'Valor do campo do CSV', + 'csv_field_mapped_to' => 'Deve ser mapeado para...', + 'csv_do_not_map' => 'Não mapear este valor', + 'csv_download_config_title' => 'Download do CSV de configuração ', + 'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.', + 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.', + 'csv_do_download_config' => 'Download do arquivo de configuração.', + 'csv_empty_description' => '(descrição vazia)', + 'csv_upload_form' => 'Formulário de Upload do CSV', + 'csv_index_unsupported_warning' => 'O importador de CSV está incapaz de fazer o seguinte:', + 'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.', + 'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".', + 'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.', + 'csv_process_title' => 'Importação do CSV terminou!', + 'csv_process_text' => 'O importador do CSV terminou e processou :rows linhas', + 'csv_row' => 'Linha', + 'csv_import_with_errors' => 'Houve um erro.|Houve :errors erros.', + 'csv_error_see_logs' => 'Verifique o arquivo de log para ver detalhes.', + 'csv_process_new_entries' => 'Firefly criou :imported nova(s) transação(ões)', + 'csv_start_over' => 'Importar novamente', + 'csv_to_index' => 'Voltar para tela inicial', + 'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload', + 'csv_column__ignore' => '(ignorar esta coluna)', + 'csv_column_account-iban' => 'Conta de Ativo (IBAN)', + 'csv_column_account-id' => 'ID da Conta de Ativo (correspondente Firefly)', + 'csv_column_account-name' => 'Conta de Ativo (nome)', + 'csv_column_amount' => 'Valor', + 'csv_column_bill-id' => 'ID Fatura (correspondente Firefly)', + 'csv_column_bill-name' => 'Nom da Fatura', + 'csv_column_budget-id' => 'ID do Orçamento (correspondente Firefly)', + 'csv_column_budget-name' => 'Nome do Orçamento', + 'csv_column_category-id' => 'ID da Categoria (correspondente Firefly)', + 'csv_column_category-name' => 'Nome da Categoria', + 'csv_column_currency-code' => 'Código da Moeda (ISO 4217)', + 'csv_column_currency-id' => 'ID da Moeda (correspondente Firefly)', + 'csv_column_currency-name' => 'Nome da Moeda (correspondente Firefly)', + 'csv_column_currency-symbol' => 'Símbolo da Moeda (correspondente Firefly)', + 'csv_column_date-rent' => 'Rent calculation date', + 'csv_column_date-transaction' => 'Data', + 'csv_column_description' => 'Descrição', + 'csv_column_opposing-iban' => 'Opposing account (IBAN)', + 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'csv_column_opposing-name' => 'Opposing account (name)', + 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'csv_column_sepa-db' => 'SEPA Direct Debet', + 'csv_column_tags-comma' => 'Tags (separadas por vírgula)', + 'csv_column_tags-space' => 'Tags (separadas por espaço)', + 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', + 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', + 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', // create new stuff: - 'create_new_withdrawal' => 'Criar nova retirada', - 'create_new_deposit' => 'Criar um novo depósito', - 'create_new_transfer' => 'Criar nova transferência', - 'create_new_asset' => 'Criar nova conta de ativo', - 'create_new_expense' => 'Criar nova conta de despesa', - 'create_new_revenue' => 'Criar nova conta de receita', - 'create_new_piggy_bank' => 'Criar novo cofrinho', - 'create_new_bill' => 'Criar nova fatura', + 'create_new_withdrawal' => 'Criar nova retirada', + 'create_new_deposit' => 'Criar um novo depósito', + 'create_new_transfer' => 'Criar nova transferência', + 'create_new_asset' => 'Criar nova conta de ativo', + 'create_new_expense' => 'Criar nova conta de despesa', + 'create_new_revenue' => 'Criar nova conta de receita', + 'create_new_piggy_bank' => 'Criar novo cofrinho', + 'create_new_bill' => 'Criar nova fatura', // currencies: - 'create_currency' => 'Criar uma nova moeda', - 'edit_currency' => 'Editar moeda ":name"', - 'store_currency' => 'Armazenar nova moeda', - 'update_currency' => 'Atualizar moeda', + 'create_currency' => 'Criar uma nova moeda', + 'edit_currency' => 'Editar moeda ":name"', + 'store_currency' => 'Armazenar nova moeda', + 'update_currency' => 'Atualizar moeda', // new user: - 'submit' => 'Enviar', - 'getting_started' => 'Iniciar', - 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', - 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', - 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', + 'submit' => 'Enviar', + 'getting_started' => 'Iniciar', + 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', + 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', + 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', // forms: - 'mandatoryFields' => 'Campos obrigatórios', - 'optionalFields' => 'Campos opcionais', - 'options' => 'Opções', - 'something' => 'Qualquer coisa!', + 'mandatoryFields' => 'Campos obrigatórios', + 'optionalFields' => 'Campos opcionais', + 'options' => 'Opções', + 'something' => 'Qualquer coisa!', // budgets: - 'create_new_budget' => 'Criar um novo orçamento', - 'store_new_budget' => 'Armazenar novo orçamento', - 'availableIn' => 'Disponível em :date', - 'transactionsWithoutBudget' => 'Despesas sem orçamentos', - 'transactionsWithoutBudgetDate' => 'Despesas sem orçamentos em :date', - 'createBudget' => 'Novo orçamento', - 'inactiveBudgets' => 'Orçamentos inativos', - 'without_budget_between' => 'Transactions without a budget between :start and :end', - 'budget_in_month' => ':name no :month', - 'delete_budget' => 'Delete budget ":name"', - 'edit_budget' => 'Edit budget ":name"', - 'update_amount' => 'Update amount', - 'update_budget' => 'Update budget', + 'create_new_budget' => 'Criar um novo orçamento', + 'store_new_budget' => 'Armazenar novo orçamento', + 'availableIn' => 'Disponível em :date', + 'transactionsWithoutBudget' => 'Despesas sem orçamentos', + 'transactionsWithoutBudgetDate' => 'Despesas sem orçamentos em :date', + 'createBudget' => 'Novo orçamento', + 'inactiveBudgets' => 'Orçamentos inativos', + 'without_budget_between' => 'Transactions without a budget between :start and :end', + 'budget_in_month' => ':name no :month', + 'delete_budget' => 'Delete budget ":name"', + 'edit_budget' => 'Edit budget ":name"', + 'update_amount' => 'Update amount', + 'update_budget' => 'Update budget', // bills: - 'delete_bill' => 'Delete bill ":name"', - 'edit_bill' => 'Edit bill ":name"', - 'update_bill' => 'Update bill', - 'store_new_bill' => 'Store new bill', + 'delete_bill' => 'Delete bill ":name"', + 'edit_bill' => 'Edit bill ":name"', + 'update_bill' => 'Update bill', + 'store_new_bill' => 'Store new bill', // accounts: - 'details_for_asset' => 'Details for asset account ":name"', - 'details_for_expense' => 'Details for expense account ":name"', - 'details_for_revenue' => 'Details for revenue account ":name"', - 'details_for_cash' => 'Details for cash account ":name"', - 'store_new_asset_account' => 'Store new asset account', - 'store_new_expense_account' => 'Store new expense account', - 'store_new_revenue_account' => 'Store new revenue account', - 'edit_asset_account' => 'Edit asset account ":name"', - 'edit_expense_account' => 'Edit expense account ":name"', - 'edit_revenue_account' => 'Edit revenue account ":name"', - 'delete_asset_account' => 'Delete asset account ":name"', - 'delete_expense_account' => 'Delete expense account ":name"', - 'delete_revenue_account' => 'Delete revenue account ":name"', - 'asset_deleted' => 'Successfully deleted asset account ":name"', - 'expense_deleted' => 'Successfully deleted expense account ":name"', - 'revenue_deleted' => 'Successfully deleted revenue account ":name"', - 'update_asset_account' => 'Update asset account', - 'update_expense_account' => 'Update expense account', - 'update_revenue_account' => 'Update revenue account', - 'make_new_asset_account' => 'Create a new asset account', - 'make_new_expense_account' => 'Create a new expense account', - 'make_new_revenue_account' => 'Create a new revenue account', - 'asset_accounts' => 'Asset accounts', - 'expense_accounts' => 'Expense accounts', - 'revenue_accounts' => 'Revenue accounts', - 'accountExtraHelp_asset' => '', - 'accountExtraHelp_expense' => '', - 'accountExtraHelp_revenue' => '', - 'account_type' => 'Account type', - 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', + 'details_for_asset' => 'Details for asset account ":name"', + 'details_for_expense' => 'Details for expense account ":name"', + 'details_for_revenue' => 'Details for revenue account ":name"', + 'details_for_cash' => 'Details for cash account ":name"', + 'store_new_asset_account' => 'Store new asset account', + 'store_new_expense_account' => 'Store new expense account', + 'store_new_revenue_account' => 'Store new revenue account', + 'edit_asset_account' => 'Edit asset account ":name"', + 'edit_expense_account' => 'Edit expense account ":name"', + 'edit_revenue_account' => 'Edit revenue account ":name"', + 'delete_asset_account' => 'Delete asset account ":name"', + 'delete_expense_account' => 'Delete expense account ":name"', + 'delete_revenue_account' => 'Delete revenue account ":name"', + 'asset_deleted' => 'Successfully deleted asset account ":name"', + 'expense_deleted' => 'Successfully deleted expense account ":name"', + 'revenue_deleted' => 'Successfully deleted revenue account ":name"', + 'update_asset_account' => 'Update asset account', + 'update_expense_account' => 'Update expense account', + 'update_revenue_account' => 'Update revenue account', + 'make_new_asset_account' => 'Create a new asset account', + 'make_new_expense_account' => 'Create a new expense account', + 'make_new_revenue_account' => 'Create a new revenue account', + 'asset_accounts' => 'Asset accounts', + 'expense_accounts' => 'Expense accounts', + 'revenue_accounts' => 'Revenue accounts', + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => '', + 'accountExtraHelp_revenue' => '', + 'account_type' => 'Account type', + 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', // categories: - 'new_category' => 'New category', - 'create_new_category' => 'Create a new category', - 'without_category' => 'Without a category', - 'update_category' => 'Wijzig categorie', - 'categories' => 'Categories', - 'edit_category' => 'Edit category ":name"', - 'no_category' => '(no category)', - 'category' => 'Category', - 'delete_category' => 'Delete category ":name"', - 'store_category' => 'Store new category', + 'new_category' => 'New category', + 'create_new_category' => 'Create a new category', + 'without_category' => 'Without a category', + 'update_category' => 'Wijzig categorie', + 'categories' => 'Categories', + 'edit_category' => 'Edit category ":name"', + 'no_category' => '(no category)', + 'category' => 'Category', + 'delete_category' => 'Delete category ":name"', + 'store_category' => 'Store new category', + 'without_category_between' => 'Without category between :start and :end', // transactions: - 'update_withdrawal' => 'Update withdrawal', - 'update_deposit' => 'Update deposit', - 'update_transfer' => 'Update transfer', - 'delete_withdrawal' => 'Delete withdrawal ":description"', - 'delete_deposit' => 'Delete deposit ":description"', - 'delete_transfer' => 'Delete transfer ":description"', + 'update_withdrawal' => 'Update withdrawal', + 'update_deposit' => 'Update deposit', + 'update_transfer' => 'Update transfer', + 'delete_withdrawal' => 'Delete withdrawal ":description"', + 'delete_deposit' => 'Delete deposit ":description"', + 'delete_transfer' => 'Delete transfer ":description"', // new user: - 'welcome' => 'Welcome to Firefly!', - 'createNewAsset' => 'Create a new asset account to get started. ' . - 'This will allow you to create transactions and start your financial management', - 'createNewAssetButton' => 'Criar nova conta de ativo', + 'welcome' => 'Welcome to Firefly!', + 'createNewAsset' => 'Create a new asset account to get started. ' . + 'This will allow you to create transactions and start your financial management', + 'createNewAssetButton' => 'Criar nova conta de ativo', // home page: - 'yourAccounts' => 'Your accounts', - 'budgetsAndSpending' => 'Budgets and spending', - 'savings' => 'Savings', - 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', - 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', - 'newWithdrawal' => 'New expense', - 'newDeposit' => 'Novo depósito', - 'newTransfer' => 'Nova transferência', - 'moneyIn' => 'Money in', - 'moneyOut' => 'Money out', - 'billsToPay' => 'Bills to pay', - 'billsPaid' => 'Bills paid', - 'viewDetails' => 'View details', - 'divided' => 'divided', - 'toDivide' => 'left to divide', + 'yourAccounts' => 'Your accounts', + 'budgetsAndSpending' => 'Budgets and spending', + 'savings' => 'Savings', + 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', + 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', + 'newWithdrawal' => 'New expense', + 'newDeposit' => 'Novo depósito', + 'newTransfer' => 'Nova transferência', + 'moneyIn' => 'Money in', + 'moneyOut' => 'Money out', + 'billsToPay' => 'Bills to pay', + 'billsPaid' => 'Bills paid', + 'viewDetails' => 'View details', + 'divided' => 'divided', + 'toDivide' => 'left to divide', // menu and titles, should be recycled as often as possible: - 'toggleNavigation' => 'Toggle navigation', - 'currency' => 'Currency', - 'preferences' => 'Preferences', - 'logout' => 'Logout', - 'searchPlaceholder' => 'Search...', - 'dashboard' => 'Dashboard', - 'currencies' => 'Currencies', - 'accounts' => 'Accounts', - 'Asset account' => 'Asset account', - 'Default account' => 'Asset account', - 'Expense account' => 'Conta de Despesa', - 'Revenue account' => 'Conta de Receita', - 'Initial balance account' => 'Initial balance account', - 'budgets' => 'Budgets', - 'tags' => 'Tags', - 'reports' => 'Relatórios', - 'transactions' => 'Transações', - 'expenses' => 'Despesas', - 'income' => 'Receita / Renda', - 'transfers' => 'Transferências', - 'moneyManagement' => 'Gerenciamento de Dinheiro', - 'piggyBanks' => 'Cofrinhos', - 'bills' => 'Faturas', - 'createNew' => 'Criar nova(o)', - 'withdrawal' => 'Retirada', - 'deposit' => 'Depósito', - 'account' => 'Conta', - 'transfer' => 'Transferência', - 'Withdrawal' => 'Retirada', - 'Deposit' => 'Depósito', - 'Transfer' => 'Transferência', - 'bill' => 'Fatura', - 'yes' => 'Sim', - 'no' => 'Não', - 'amount' => 'Valor', - 'newBalance' => 'Novo saldo', - 'overview' => 'Visão Geral', - 'saveOnAccount' => 'Salvar na conta', - 'unknown' => 'Desconhecido', - 'daily' => 'Diário', - 'weekly' => 'Semanal', - 'monthly' => 'Mensal', - 'quarterly' => 'Trimestral', - 'half-year' => 'Semestral', - 'yearly' => 'Anual', - 'profile' => 'Perfil', + 'toggleNavigation' => 'Toggle navigation', + 'currency' => 'Currency', + 'preferences' => 'Preferences', + 'logout' => 'Logout', + 'searchPlaceholder' => 'Search...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Currencies', + 'accounts' => 'Accounts', + 'Asset account' => 'Asset account', + 'Default account' => 'Asset account', + 'Expense account' => 'Conta de Despesa', + 'Revenue account' => 'Conta de Receita', + 'Initial balance account' => 'Initial balance account', + 'budgets' => 'Budgets', + 'tags' => 'Tags', + 'reports' => 'Relatórios', + 'transactions' => 'Transações', + 'expenses' => 'Despesas', + 'income' => 'Receita / Renda', + 'transfers' => 'Transferências', + 'moneyManagement' => 'Gerenciamento de Dinheiro', + 'piggyBanks' => 'Cofrinhos', + 'bills' => 'Faturas', + 'createNew' => 'Criar nova(o)', + 'withdrawal' => 'Retirada', + 'deposit' => 'Depósito', + 'account' => 'Conta', + 'transfer' => 'Transferência', + 'Withdrawal' => 'Retirada', + 'Deposit' => 'Depósito', + 'Transfer' => 'Transferência', + 'bill' => 'Fatura', + 'yes' => 'Sim', + 'no' => 'Não', + 'amount' => 'Valor', + 'newBalance' => 'Novo saldo', + 'overview' => 'Visão Geral', + 'saveOnAccount' => 'Salvar na conta', + 'unknown' => 'Desconhecido', + 'daily' => 'Diário', + 'weekly' => 'Semanal', + 'monthly' => 'Mensal', + 'quarterly' => 'Trimestral', + 'half-year' => 'Semestral', + 'yearly' => 'Anual', + 'profile' => 'Perfil', // reports: - 'report_default' => 'Default financial report for :start until :end', - 'quick_link_reports' => 'Quick links', - 'quick_link_default_report' => 'Default financial report', - 'report_this_month_quick' => 'Current month, all accounts', - 'report_this_year_quick' => 'Current year, all accounts', - 'report_all_time_quick' => 'All-time, all accounts', - 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', - 'incomeVsExpenses' => 'Renda vs. Despesas', - 'accountBalances' => 'Saldos de Contas', - 'balanceStartOfYear' => 'Balance at start of year', - 'balanceEndOfYear' => 'Balance at end of year', - 'balanceStartOfMonth' => 'Balance at start of month', - 'balanceEndOfMonth' => 'Balance at end of month', - 'balanceStart' => 'Balance at start of period', - 'balanceEnd' => 'Balance at end of period', - 'reportsOwnAccounts' => 'Reports for your own accounts', - 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', - 'splitByAccount' => 'Split by account', - 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', - 'coveredWithTags' => 'Covered with tags', - 'leftUnbalanced' => 'Left unbalanced', - 'expectedBalance' => 'Saldo Experado', - 'outsideOfBudgets' => 'Fora do orçamento', - 'leftInBudget' => 'Deixou no orçamento', - 'sumOfSums' => 'Soma dos montantes', - 'noCategory' => '(no category)', - 'notCharged' => 'Não cobrado (ainda)', - 'inactive' => 'Inativo', - 'difference' => 'Diferente', - 'in' => 'Entrada', - 'out' => 'Saída', - 'topX' => 'top :number', - 'showTheRest' => 'Mostrar tudo', - 'hideTheRest' => 'Mostrar apenas os top :number', - 'sum_of_year' => 'Soma do ano', - 'sum_of_years' => 'Sum of years', - 'average_of_year' => 'Média do ano', - 'average_of_years' => 'Average of years', - 'categories_earned_in_year' => 'Categories (by earnings)', - 'categories_spent_in_year' => 'Categories (by spendings)', - 'report_type' => 'Report type', - 'report_type_default' => 'Default financial report', - 'report_included_accounts' => 'Included accounts', - 'report_date_range' => 'Date range', - 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', - 'report_preset_ranges' => 'Pre-set ranges', - 'shared' => 'Shared', + 'report_default' => 'Default financial report for :start until :end', + 'quick_link_reports' => 'Quick links', + 'quick_link_default_report' => 'Default financial report', + 'report_this_month_quick' => 'Current month, all accounts', + 'report_this_year_quick' => 'Current year, all accounts', + 'report_all_time_quick' => 'All-time, all accounts', + 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', + 'incomeVsExpenses' => 'Renda vs. Despesas', + 'accountBalances' => 'Saldos de Contas', + 'balanceStartOfYear' => 'Balance at start of year', + 'balanceEndOfYear' => 'Balance at end of year', + 'balanceStartOfMonth' => 'Balance at start of month', + 'balanceEndOfMonth' => 'Balance at end of month', + 'balanceStart' => 'Balance at start of period', + 'balanceEnd' => 'Balance at end of period', + 'reportsOwnAccounts' => 'Reports for your own accounts', + 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'splitByAccount' => 'Split by account', + 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', + 'coveredWithTags' => 'Covered with tags', + 'leftUnbalanced' => 'Left unbalanced', + 'expectedBalance' => 'Saldo Experado', + 'outsideOfBudgets' => 'Fora do orçamento', + 'leftInBudget' => 'Deixou no orçamento', + 'sumOfSums' => 'Soma dos montantes', + 'noCategory' => '(no category)', + 'notCharged' => 'Não cobrado (ainda)', + 'inactive' => 'Inativo', + 'difference' => 'Diferente', + 'in' => 'Entrada', + 'out' => 'Saída', + 'topX' => 'top :number', + 'showTheRest' => 'Mostrar tudo', + 'hideTheRest' => 'Mostrar apenas os top :number', + 'sum_of_year' => 'Soma do ano', + 'sum_of_years' => 'Sum of years', + 'average_of_year' => 'Média do ano', + 'average_of_years' => 'Average of years', + 'categories_earned_in_year' => 'Categories (by earnings)', + 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_type' => 'Report type', + 'report_type_default' => 'Default financial report', + 'report_included_accounts' => 'Included accounts', + 'report_date_range' => 'Date range', + 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', + 'report_preset_ranges' => 'Pre-set ranges', + 'shared' => 'Shared', // charts: - 'dayOfMonth' => 'Dia do mês', - 'month' => 'Mês', - 'budget' => 'Orçamento', - 'spent' => 'Gasto', - 'earned' => 'Ganho', - 'overspent' => 'Gasto excedido', - 'left' => 'Left', - 'noBudget' => '(sem orçamento)', - 'maxAmount' => 'Valor Máximo', - 'minAmount' => 'Valor Mínimo', - 'billEntry' => 'Current bill entry', - 'name' => 'Nome', - 'date' => 'Data', - 'paid' => 'Pago', - 'unpaid' => 'Não pago', - 'day' => 'Dia', - 'budgeted' => 'Orçado', - 'period' => 'Período', - 'balance' => 'Saldo', - 'summary' => 'Sumário', - 'sum' => 'Soma', - 'average' => 'Média', - 'balanceFor' => 'Saldo para ":name"', + 'dayOfMonth' => 'Dia do mês', + 'month' => 'Mês', + 'budget' => 'Orçamento', + 'spent' => 'Gasto', + 'earned' => 'Ganho', + 'overspent' => 'Gasto excedido', + 'left' => 'Left', + 'noBudget' => '(sem orçamento)', + 'maxAmount' => 'Valor Máximo', + 'minAmount' => 'Valor Mínimo', + 'billEntry' => 'Current bill entry', + 'name' => 'Nome', + 'date' => 'Data', + 'paid' => 'Pago', + 'unpaid' => 'Não pago', + 'day' => 'Dia', + 'budgeted' => 'Orçado', + 'period' => 'Período', + 'balance' => 'Saldo', + 'summary' => 'Sumário', + 'sum' => 'Soma', + 'average' => 'Média', + 'balanceFor' => 'Saldo para ":name"', // piggy banks: - 'piggy_bank' => 'Cofrinho', - 'new_piggy_bank' => 'Criar novo cofrinho', - 'store_piggy_bank' => 'Store new piggy bank', - 'account_status' => 'Account status', - 'left_for_piggy_banks' => 'Left for piggy banks', - 'sum_of_piggy_banks' => 'Sum of piggy banks', - 'saved_so_far' => 'Saved so far', - 'left_to_save' => 'Left to save', - 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', - 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', - 'add' => 'Adicionar', - 'remove' => 'Remover', - 'max_amount_add' => 'The maximum amount you can add is', - 'max_amount_remove' => 'The maximum amount you can remove is', - 'update_piggy_button' => 'Update piggy bank', - 'update_piggy_title' => 'Update piggy bank ":name"', - 'details' => 'Detalhes', - 'events' => 'Eventos', - 'target_amount' => 'Valor alvo', - 'start_date' => 'Data de Início', - 'target_date' => 'Data Alvo', - 'no_target_date' => 'Nenhum data', - 'todo' => 'A fazer', - 'table' => 'Tabela', - 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', - 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', - 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', - 'delete_piggy_bank' => 'Apagar cofrinho ":name"', + 'piggy_bank' => 'Cofrinho', + 'new_piggy_bank' => 'Criar novo cofrinho', + 'store_piggy_bank' => 'Store new piggy bank', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Adicionar', + 'remove' => 'Remover', + 'max_amount_add' => 'The maximum amount you can add is', + 'max_amount_remove' => 'The maximum amount you can remove is', + 'update_piggy_button' => 'Update piggy bank', + 'update_piggy_title' => 'Update piggy bank ":name"', + 'details' => 'Detalhes', + 'events' => 'Eventos', + 'target_amount' => 'Valor alvo', + 'start_date' => 'Data de Início', + 'target_date' => 'Data Alvo', + 'no_target_date' => 'Nenhum data', + 'todo' => 'A fazer', + 'table' => 'Tabela', + 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', + 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', + 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', + 'delete_piggy_bank' => 'Apagar cofrinho ":name"', // tags - 'regular_tag' => 'Just a regular tag.', - 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', - 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', - 'delete_tag' => 'Apagar tag ":tag"', - 'new_tag' => 'Fazer nova tag', - 'edit_tag' => 'Editar tag ":tag"', - 'no_year' => 'Nenhum ano definido', - 'no_month' => 'Nenhum mês definido', - 'tag_title_nothing' => 'Tags padrões', - 'tag_title_balancingAct' => 'Balancing act tags', - 'tag_title_advancePayment' => 'Advance payment tags', - 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', - 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible. ', - 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', + 'regular_tag' => 'Just a regular tag.', + 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', + 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', + 'delete_tag' => 'Apagar tag ":tag"', + 'new_tag' => 'Fazer nova tag', + 'edit_tag' => 'Editar tag ":tag"', + 'no_year' => 'Nenhum ano definido', + 'no_month' => 'Nenhum mês definido', + 'tag_title_nothing' => 'Tags padrões', + 'tag_title_balancingAct' => 'Balancing act tags', + 'tag_title_advancePayment' => 'Advance payment tags', + 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', + 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', + 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', ]; diff --git a/resources/lang/pt_BR/form.php b/resources/lang/pt_BR/form.php old mode 100644 new mode 100755 index 03f40ccc90..fa7c85df17 --- a/resources/lang/pt_BR/form.php +++ b/resources/lang/pt_BR/form.php @@ -53,6 +53,7 @@ return [ 'csv_config' => 'Importar CSV de configuração', 'specifix' => 'Bank- or file specific fixes', 'csv_import_account' => 'Default import account', + 'csv_delimiter' => 'CSV field delimiter', 'attachments[]' => 'Anexos', 'store_new_withdrawal' => 'Store new withdrawal', 'store_new_deposit' => 'Store new deposit', @@ -67,6 +68,12 @@ return [ 'filename' => 'Nome do arquivo', 'mime' => 'Mime type', 'size' => 'Tamanho', + 'trigger' => 'Trigger', + 'stop_processing' => 'Stop processing', + + 'csv_comma' => 'A comma (,)', + 'csv_semicolon' => 'A semicolon (;)', + 'csv_tab' => 'A tab (invisible)', 'delete_account' => 'Apagar conta ":name"', @@ -76,10 +83,14 @@ return [ 'delete_currency' => 'Delete currency ":name"', 'delete_journal' => 'Delete transaction with description ":description"', 'delete_attachment' => 'Apagar anexo ":name"', + 'delete_rule' => 'Delete rule ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?', 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', 'bill_areYouSure' => 'Você tem certeza que quer apagar a fatura ":name"?', + 'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?', + 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?', 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', @@ -89,6 +100,7 @@ return [ 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', + 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.', 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', diff --git a/resources/lang/pt_BR/help.php b/resources/lang/pt_BR/help.php old mode 100644 new mode 100755 index ca6f66c753..99e2295f4f --- a/resources/lang/pt_BR/help.php +++ b/resources/lang/pt_BR/help.php @@ -20,74 +20,65 @@ return [ 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', - 'register' => 'register', - 'index' => 'The main index', - 'home' => 'home', - 'flush' => 'flush', - 'accounts-index' => 'accounts.index', - 'accounts-create' => 'accounts.create', - 'accounts-edit' => 'accounts.edit', - 'accounts-delete' => 'accounts.delete', - 'accounts-show' => 'accounts.show', - 'bills-index' => 'bills.index', - 'bills-rescan' => 'bills.rescan', - 'bills-create' => 'bills.create', - 'bills-edit' => 'bills.edit', - 'bills-delete' => 'bills.delete', - 'bills-show' => 'bills.show', - 'budgets-index' => 'budgets.index', - 'budgets-income' => 'budgets.income', - 'budgets-create' => 'budgets.create', - 'budgets-edit' => 'budgets.edit', - 'budgets-delete' => 'budgets.delete', - 'budgets-show' => 'budgets.show', - 'budgets-noBudget' => 'budgets.noBudget', - 'categories-index' => 'categories.index', - 'categories-create' => 'categories.create', - 'categories-edit' => 'categories.edit', - 'categories-delete' => 'categories.delete', - 'categories-show' => 'categories.show', - 'categories-noCategory' => 'categories.noCategory', - 'csv-index' => 'Carregar e importar um arquivo CSV', - 'currency-index' => 'currency.index', - 'currency-create' => 'currency.create', - 'currency-edit' => 'currency.edit', - 'currency-delete' => 'currency.delete', - 'currency-default' => 'currency.default', - 'help-show' => 'help.show', - 'json-expense-accounts' => 'json.expense-accounts', - 'json-revenue-accounts' => 'json.revenue-accounts', - 'json-categories' => 'json.categories', - 'json-tags' => 'json.tags', - 'json-box-in' => 'json.box.in', - 'json-box-out' => 'json.box.out', - 'json-box-paid' => 'json.box.paid', - 'json-box-unpaid' => 'json.box.unpaid', - 'new-user-index' => 'new-user.index', - 'piggy-banks-index' => 'piggy-banks.index', - 'piggy-banks-addMoney' => 'piggy-banks.addMoney', - 'piggy-banks-removeMoney' => 'piggy-banks.removeMoney', - 'piggy-banks-create' => 'piggy-banks.create', - 'piggy-banks-edit' => 'piggy-banks.edit', - 'piggy-banks-delete' => 'piggy-banks.delete', - 'piggy-banks-show' => 'piggy-banks.show', - 'preferences' => 'preferences', - 'profile' => 'profile', - 'profile-change-password' => 'profile.change-password', - 'profile-delete-account' => 'profile.delete-account', - 'reports-index' => 'reports.index', - 'reports-year' => 'reports.year', - 'reports-month' => 'reports.month', - 'search' => 'search', - 'tags-index' => 'tags.index', - 'tags-create' => 'tags.create', - 'tags-show' => 'tags.show', - 'tags-edit' => 'tags.edit', - 'tags-delete' => 'tags.delete', - 'transactions-index' => 'transactions.index', - 'transactions-create' => 'transactions.create', - 'transactions-edit' => 'transactions.edit', - 'transactions-delete' => 'transactions.delete', - 'transactions-show' => 'transactions.show', - 'logout' => 'logout', + 'index' => 'index', + 'home' => 'home', + 'accounts-index' => 'accounts.index', + 'accounts-create' => 'accounts.create', + 'accounts-edit' => 'accounts.edit', + 'accounts-delete' => 'accounts.delete', + 'accounts-show' => 'accounts.show', + 'attachments-edit' => 'attachments.edit', + 'attachments-delete' => 'attachments.delete', + 'attachments-show' => 'attachments.show', + 'attachments-preview' => 'attachments.preview', + 'bills-index' => 'bills.index', + 'bills-create' => 'bills.create', + 'bills-edit' => 'bills.edit', + 'bills-delete' => 'bills.delete', + 'bills-show' => 'bills.show', + 'budgets-index' => 'budgets.index', + 'budgets-create' => 'budgets.create', + 'budgets-edit' => 'budgets.edit', + 'budgets-delete' => 'budgets.delete', + 'budgets-show' => 'budgets.show', + 'budgets-noBudget' => 'budgets.noBudget', + 'categories-index' => 'categories.index', + 'categories-create' => 'categories.create', + 'categories-edit' => 'categories.edit', + 'categories-delete' => 'categories.delete', + 'categories-show' => 'categories.show', + 'categories-show-date' => 'categories.show.date', + 'categories-noCategory' => 'categories.noCategory', + 'csv-index' => 'Carregar e importar um arquivo CSV', + 'csv-column-roles' => 'csv.column-roles', + 'csv-map' => 'csv.map', + 'csv-download-config-page' => 'csv.download-config-page', + 'csv-process' => 'csv.process', + 'currency-index' => 'currency.index', + 'currency-create' => 'currency.create', + 'currency-edit' => 'currency.edit', + 'currency-delete' => 'currency.delete', + 'new-user-index' => 'new-user.index', + 'piggy-banks-index' => 'piggy-banks.index', + 'piggy-banks-create' => 'piggy-banks.create', + 'piggy-banks-edit' => 'piggy-banks.edit', + 'piggy-banks-delete' => 'piggy-banks.delete', + 'piggy-banks-show' => 'piggy-banks.show', + 'preferences' => 'preferences', + 'profile' => 'profile', + 'profile-change-password' => 'profile.change-password', + 'profile-delete-account' => 'profile.delete-account', + 'reports-index' => 'reports.index', + 'reports-report' => 'reports.report', + 'search' => 'search', + 'tags-index' => 'tags.index', + 'tags-create' => 'tags.create', + 'tags-show' => 'tags.show', + 'tags-edit' => 'tags.edit', + 'tags-delete' => 'tags.delete', + 'transactions-index' => 'transactions.index', + 'transactions-create' => 'transactions.create', + 'transactions-edit' => 'transactions.edit', + 'transactions-delete' => 'transactions.delete', + 'transactions-show' => 'transactions.show', ]; diff --git a/resources/lang/pt_BR/list.php b/resources/lang/pt_BR/list.php old mode 100644 new mode 100755 diff --git a/resources/lang/pt_BR/pagination.php b/resources/lang/pt_BR/pagination.php old mode 100644 new mode 100755 diff --git a/resources/lang/pt_BR/passwords.php b/resources/lang/pt_BR/passwords.php old mode 100644 new mode 100755 diff --git a/resources/lang/pt_BR/validation.php b/resources/lang/pt_BR/validation.php index 7819d30ce1..05c224dc29 100755 --- a/resources/lang/pt_BR/validation.php +++ b/resources/lang/pt_BR/validation.php @@ -1,6 +1,8 @@ 'This value is invalid for the selected trigger.', + 'rule_action_value' => 'This value is invalid for the selected action.', 'invalid_domain' => 'Due to security constraints, you cannot register from this domain.', 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', 'file_attached' => 'Succesfully uploaded file ":name".', From a755badd5f5d6e92aff1dceef7a4c3955019eb53 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 16:33:54 +0100 Subject: [PATCH 164/276] Use correct parameter. --- app/Http/Controllers/RuleController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 647c55e86d..37b79fdd7a 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -54,7 +54,7 @@ class RuleController extends Controller // process the rule itself: $data = [ - 'rule_group_id' => intval($request->get('rule_group_id')), + 'rule_group_id' => $ruleGroup->id, 'title' => $request->get('title'), 'trigger' => $request->get('trigger'), 'description' => $request->get('description'), From 2ff806dedc999025bf83fd45324cfcf020d660e7 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Fri, 15 Jan 2016 16:48:09 +0100 Subject: [PATCH 165/276] Initial version of ABN AMRO import specifix with amount correction and SEPA description parser --- .../Csv/Specifix/AbnAmroDescription.php | 183 ++++++++++++++++++ config/csv.php | 1 + 2 files changed, 184 insertions(+) create mode 100644 app/Helpers/Csv/Specifix/AbnAmroDescription.php diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php new file mode 100644 index 0000000000..890bbeff4e --- /dev/null +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -0,0 +1,183 @@ +handleAmount(); + $this->parseSepaDescription(); + + return $this->data; + + } + + /** + * @param array $data + */ + public function setData($data) + { + $this->data = $data; + } + + /** + * @param array $row + */ + public function setRow($row) + { + $this->row = $row; + } + + protected function handleAmount() { + $this->data['amount'] = floatval(str_replace(',', '.', $this->row[6])); + } + + /** + * Parses the current description in SEPA format + * @return boolean true if the description is SEPA format, false otherwise + */ + protected function parseSepaDescription() + { + // See if the current description is formatted as a SEPA plain description + if( preg_match( "/^SEPA(.{28})/", $this->data[ "description" ], $matches ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as SEPA plain description.'); + $type = trim($matches[1]); + + // SEPA plain descriptions contain several key-value pairs, split by a colon + preg_match_all( "/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9.-]+(?=\s))/", $this->data[ "description" ], $matches, PREG_SET_ORDER ); + + foreach( $matches as $match ) { + $key = $match[1]; + $value = trim($match[2]); + + switch( strtoupper($key) ) { + case 'OMSCHRIJVING': + $this->data['description'] = $value; + break; + case 'NAAM': + $this->data['opposing-account-name'] = $value; + break; + case 'IBAN': + $this->data['opposing-account-iban'] = $value; + break; + default: + // Ignore the rest + } + } + + // Add the type to the description + $this->data['description'] .= ' (' . $type . ')'; + + return true; + } + + return false; + } + +/*** + * +def ParseDescription(desc): + values = None + ### SEPA PLAIN: SEPA iDEAL IBAN: NL12RABO0121212212 BIC: RABONL2U Naam: Silver Ocean B.V. Omschrijving: 1232138 1232131233 412321 iBOOD.com iBOOD.com B.V. Kenmerk: 12-12-2014 21:03 002000 0213123238 + sepa = re.findall(r"(?P^SEPA.{28})", desc, re.I) + if (sepa): + values = {} + value = sepa[0] + values["TRTP"] = value.strip() + values["EREF"] = "" + values["REMI"] = "" + sepa = re.findall(r"(?P[A-Za-z]+(?=:\s)):\s(?P[A-Za-z 0-9.-]+(?=\s))", desc, re.I) + for line in sepa: + key = line[0] + if key.upper() == 'OMSCHRIJVING': + key = 'REMI' + if key.upper() == 'KENMERK': + key = 'EREF' + if key.upper() == 'NAAM': + key = 'NAME' + value = line[1] + values[key] = value.strip() + # print (values) + # continue + if len(values["REMI"]) > 19: + values["REMI"] = values["REMI"][0:18] + values["REMI"][19:] + if values["REMI"] == "": + values["REMI"] = values["TRTP"] + + ### TRTP ENCODED: /TRTP/SEPA OVERBOEKING/IBAN/NL23ABNA0000000000/BIC/ABNANL2A/NAME/baasd dsdsT CJ/REMI/Nullijn/EREF/NOTPROVIDED + trtp = re.findall(r"\/(?P[A-Z]{3,4})\/(?P.*?(?:(?=\/[A-Z]{3,4}\/)|$))",desc, re.I) + if (trtp): + values = {} + values["EREF"] = "" + values["REMI"] = "" + for line in trtp: + key = line[0] + value = line[1] + values[key] = value.strip() + # print (values) + # continue + if values["REMI"] == "": + values["REMI"] = values["TRTP"] + + ### BEA: BEA NR:00AJ01 31.01.01/19.54 Van HarenSchoenen132 UDE,PAS333 + trtp = re.findall(r"(?P[BG]EA) +(?PNR:[a-zA-Z:0-9]+) +(?P[0-9.\/]+) +(?P[^,]*)", desc, re.I) + if (trtp): + values = {} + values["TRTP"] = str(trtp[0][0]).strip() + values["NAME"] = str(trtp[0][3]).strip() + values["EREF"] = str(trtp[0][1]).strip() + values["DATE"] = str(trtp[0][2]).strip() + values["REMI"] = values["TRTP"] + " " + values["NAME"] + # print (values) + # continue + + ### OLD: 12.21.22.222 BNP aaaaaaa aaaaaa SCH BETALINGSKENM. 2323233232323323 MAAND* APRIL 01 REF* 1212121-42-41 + trtp = re.findall(r"^ ?(?P[0-9.]{12,15})\W+(?P.{32})", desc, re.I) + if (trtp): + values = {} + values["TRTP"] = "OLD" + values["IBAN"] = str(trtp[0][0]).strip() + values["NAME"] = str(trtp[0][1]).strip() + values["EREF"] = "" + values["REMI"] = values["TRTP"] + " " + values["NAME"] + # print (values) + # continue + ### ABN AMRO Bank N.V. Prive pakket 3,25 + abn = re.findall(r"^ABN AMRO.{24} (?P.*)", desc, re.I) + if (abn): + values = {} + values["TRTP"] = "ABN AMBRO" + values["NAME"] = "ABN AMBRO" + values["EREF"] = str(abn[0]).strip() + values["REMI"] = values["EREF"] + # print (values) + # continue + if (values == None): + # print ("Unkown: ### %s ###" % ( desc )) + return None + return values * + * + */ + +} diff --git a/config/csv.php b/config/csv.php index ba1ae72f47..d8cbd4daa5 100644 --- a/config/csv.php +++ b/config/csv.php @@ -2,6 +2,7 @@ return [ 'specifix' => [ 'RabobankDescription', + 'AbnAmroDescription', 'Dummy' ], 'post_processors' => [ From 820722f44e57e537c2add9bf3cde3fe4a16cb9fc Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 17:38:09 +0100 Subject: [PATCH 166/276] Some code cleanup courtesy of PHPStorm. --- app/Exceptions/Handler.php | 2 +- app/Http/Controllers/RuleController.php | 252 +++++++++--------- app/Models/Account.php | 11 +- app/Models/Attachment.php | 5 + app/Models/Bill.php | 7 + app/Models/Budget.php | 7 + app/Models/Category.php | 5 + app/Models/LimitRepetition.php | 5 + app/Models/PiggyBank.php | 5 + app/Models/Tag.php | 5 + app/Models/TransactionCurrency.php | 2 + .../Category/SingleCategoryRepository.php | 4 +- .../RuleGroup/RuleGroupRepository.php | 5 + .../RuleGroupRepositoryInterface.php | 2 + .../Shared/ComponentRepository.php | 2 +- app/Rules/Actions/ClearBudget.php | 3 - app/Rules/Actions/ClearCategory.php | 3 - app/Rules/Processor.php | 3 + app/Support/Amount.php | 2 +- app/Support/Binder/CategoryList.php | 1 - app/Support/Binder/Date.php | 1 + app/Support/Steam.php | 2 +- app/Support/Twig/Rule.php | 48 +++- app/Validation/FireflyValidator.php | 8 +- 24 files changed, 226 insertions(+), 164 deletions(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 1e54fff73b..7913d17568 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -40,7 +40,7 @@ class Handler extends ExceptionHandler */ public function report(Exception $e) { - return parent::report($e); + parent::report($e); } /** diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 37b79fdd7a..41489928db 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -101,44 +101,12 @@ class RuleController extends Controller // has old input? if (Input::old()) { // process old triggers. - $newIndex = 0; - foreach (Input::old('rule-trigger') as $index => $entry) { - $count = ($newIndex + 1); - $triggerCount++; - $oldTrigger = $entry; - $oldValue = Input::old('rule-trigger-value')[$index]; - $oldChecked = isset(Input::old('rule-trigger-stop')[$index]) ? true : false; - $oldTriggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => $oldTrigger, - 'oldValue' => $oldValue, - 'oldChecked' => $oldChecked, - 'count' => $count, - ] - )->render(); - $newIndex++; - } + $oldTriggers = $this->getPreviousTriggers(); + $triggerCount = count($oldTriggers); // process old actions - $newIndex = 0; - foreach (Input::old('rule-action') as $index => $entry) { - $count = ($newIndex + 1); - $actionCount++; - $oldAction = $entry; - $oldValue = Input::old('rule-action-value')[$index]; - $oldChecked = isset(Input::old('rule-action-stop')[$index]) ? true : false; - $oldActions[] = view( - 'rules.partials.action', - [ - 'oldTrigger' => $oldAction, - 'oldValue' => $oldValue, - 'oldChecked' => $oldChecked, - 'count' => $count, - ] - )->render(); - $newIndex++; - } + $oldActions = $this->getPreviousActions(); + $actionCount = count($oldActions); } $subTitleIcon = 'fa-clone'; @@ -164,107 +132,23 @@ class RuleController extends Controller */ public function edit(Rule $rule) { - - // count for current rule's triggers/actions. - $triggerCount = 0; - $actionCount = 0; - - // collection of those triggers/actions. - $oldTriggers = []; - $oldActions = []; - // has old input? if (Input::old()) { // process old triggers. - $newIndex = 0; - foreach (Input::old('rule-trigger') as $index => $entry) { - $count = ($newIndex + 1); - $triggerCount++; - $oldTrigger = $entry; - $oldValue = Input::old('rule-trigger-value')[$index]; - $oldChecked = isset(Input::old('rule-trigger-stop')[$index]) ? true : false; - $oldTriggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => $oldTrigger, - 'oldValue' => $oldValue, - 'oldChecked' => $oldChecked, - 'count' => $count, - ] - )->render(); - $newIndex++; - } + $oldTriggers = $this->getPreviousTriggers(); + $triggerCount = count($oldTriggers); // process old actions - $newIndex = 0; - foreach (Input::old('rule-action') as $index => $entry) { - $count = ($newIndex + 1); - $actionCount++; - $oldAction = $entry; - $oldValue = Input::old('rule-action-value')[$index]; - $oldChecked = isset(Input::old('rule-action-stop')[$index]) ? true : false; - $oldActions[] = view( - 'rules.partials.action', - [ - 'oldTrigger' => $oldAction, - 'oldValue' => $oldValue, - 'oldChecked' => $oldChecked, - 'count' => $count, - ] - )->render(); - $newIndex++; - } + $oldActions = $this->getPreviousActions(); + $actionCount = count($oldActions); } else { // get current triggers - $newIndex = 0; - /** - * @var int $index - * @var RuleTrigger $entry - */ - foreach ($rule->ruleTriggers as $index => $entry) { - if ($entry->trigger_type != 'user_action') { - $count = ($newIndex + 1); - $triggerCount++; - $oldTrigger = $entry->trigger_type; - $oldValue = $entry->trigger_value; - $oldChecked = $entry->stop_processing; - $oldTriggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => $oldTrigger, - 'oldValue' => $oldValue, - 'oldChecked' => $oldChecked, - 'count' => $count, - ] - )->render(); - $newIndex++; - } - - } + $oldTriggers = $this->getCurrentTriggers($rule); + $triggerCount = count($oldTriggers); // get current actions - $newIndex = 0; - /** - * @var int $index - * @var RuleAction $entry - */ - foreach ($rule->ruleActions as $index => $entry) { - $count = ($newIndex + 1); - $actionCount++; - $oldAction = $entry->action_type; - $oldValue = $entry->action_value; - $oldChecked = $entry->stop_processing; - $oldActions[] = view( - 'rules.partials.action', - [ - 'oldTrigger' => $oldAction, - 'oldValue' => $oldValue, - 'oldChecked' => $oldChecked, - 'count' => $count, - ] - )->render(); - $newIndex++; - } + $oldActions = $this->getCurrentActions($rule); + $actionCount = count($oldActions); } // get rule trigger for update / store-journal: @@ -283,9 +167,9 @@ class RuleController extends Controller return view( 'rules.rule.edit', compact( - 'rule', 'subTitle', 'primaryTrigger', - 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount' - ) + 'rule', 'subTitle', 'primaryTrigger', + 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount' + ) ); } @@ -459,5 +343,111 @@ class RuleController extends Controller } + /** + * @param Rule $rule + */ + private function getCurrentActions(Rule $rule) + { + $index = 0; + $actions = []; + + /** @var RuleAction $entry */ + foreach ($rule->ruleActions as $entry) { + $count = ($index + 1); + $actions[] = view( + 'rules.partials.action', + [ + 'oldTrigger' => $entry->action_type, + 'oldValue' => $entry->action_value, + 'oldChecked' => $entry->stop_processing, + 'count' => $count, + ] + )->render(); + $index++; + } + + return $actions; + } + + /** + * @param Rule $rule + * + * @return array + */ + private function getCurrentTriggers(Rule $rule) + { + $index = 0; + $triggers = []; + + /** @var RuleTrigger $entry */ + foreach ($rule->ruleTriggers as $entry) { + if ($entry->trigger_type != 'user_action') { + $count = ($index + 1); + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => $entry->trigger_type, + 'oldValue' => $entry->trigger_value, + 'oldChecked' => $entry->stop_processing, + 'count' => $count, + ] + )->render(); + $index++; + } + } + + return $triggers; + } + + /** + * @return array + */ + private function getPreviousActions() + { + $newIndex = 0; + $actions = []; + foreach (Input::old('rule-action') as $index => $entry) { + $count = ($newIndex + 1); + $checked = isset(Input::old('rule-action-stop')[$index]) ? true : false; + $actions[] = view( + 'rules.partials.action', + [ + 'oldTrigger' => $entry, + 'oldValue' => Input::old('rule-action-value')[$index], + 'oldChecked' => $checked, + 'count' => $count, + ] + )->render(); + $newIndex++; + } + + return $actions; + } + + /** + * @return array + */ + private function getPreviousTriggers() + { + $newIndex = 0; + $triggers = []; + foreach (Input::old('rule-trigger') as $index => $entry) { + $count = ($newIndex + 1); + $oldChecked = isset(Input::old('rule-trigger-stop')[$index]) ? true : false; + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => $entry, + 'oldValue' => Input::old('rule-trigger-value')[$index], + 'oldChecked' => $oldChecked, + 'count' => $count, + ] + )->render(); + $newIndex++; + } + + return $triggers; + } + } diff --git a/app/Models/Account.php b/app/Models/Account.php index d381f6b4c1..7545b3b9e1 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\JoinClause; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; - +use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\Account @@ -30,8 +30,8 @@ use Watson\Validating\ValidatingTrait; * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account accountTypeIn($types) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account hasMetaValue($name, $value) + * @method static Builder|\FireflyIII\Models\Account accountTypeIn($types) + * @method static Builder|\FireflyIII\Models\Account hasMetaValue($name, $value) * @property string $startBalance * @property string $endBalance */ @@ -293,6 +293,11 @@ class Account extends Model return $this->belongsTo('FireflyIII\User'); } + /** + * @param Account $value + * + * @return Account + */ public static function routeBinder(Account $value) { diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 4d90fc06a1..f57d1d31b5 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -175,6 +175,11 @@ class Attachment extends Model $this->attributes['notes'] = Crypt::encrypt($value); } + /** + * @param Attachment $value + * + * @return Attachment + */ public static function routeBinder(Attachment $value) { if (Auth::check()) { diff --git a/app/Models/Bill.php b/app/Models/Bill.php index 79cf30bbfe..4558cdb4e7 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -28,6 +28,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property boolean $match_encrypted * @property-read Collection|TransactionJournal[] $transactionjournals * @property-read User $user + * @property Carbon $nextExpectedMatch + * @property Carbon $lastFoundMatch */ class Bill extends Model { @@ -127,6 +129,11 @@ class Bill extends Model } + /** + * @param Bill $value + * + * @return Bill + */ public static function routeBinder(Bill $value) { if (Auth::check()) { diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 4000306a2c..a10bb8926d 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -23,6 +23,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read Collection|BudgetLimit[] $budgetlimits * @property-read Collection|TransactionJournal[] $transactionjournals * @property-read User $user + * @property string $dateFormatted + * @property string $budgeted */ class Budget extends Model { @@ -125,6 +127,11 @@ class Budget extends Model return $this->belongsTo('FireflyIII\User'); } + /** + * @param Budget $value + * + * @return Budget + */ public static function routeBinder(Budget $value) { if (Auth::check()) { diff --git a/app/Models/Category.php b/app/Models/Category.php index b331b5960a..50342d1785 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -112,6 +112,11 @@ class Category extends Model return $this->belongsTo('FireflyIII\User'); } + /** + * @param Category $value + * + * @return Category + */ public static function routeBinder(Category $value) { if (Auth::check()) { diff --git a/app/Models/LimitRepetition.php b/app/Models/LimitRepetition.php index bd91fbf8a6..e4c3d105eb 100644 --- a/app/Models/LimitRepetition.php +++ b/app/Models/LimitRepetition.php @@ -47,6 +47,11 @@ class LimitRepetition extends Model } + /** + * @param $value + * + * @return mixed + */ public static function routeBinder($value) { if (Auth::check()) { diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index c1c7513934..efce246557 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -122,6 +122,11 @@ class PiggyBank extends Model $this->attributes['targetamount'] = strval(round($value, 2)); } + /** + * @param PiggyBank $value + * + * @return PiggyBank + */ public static function routeBinder(PiggyBank $value) { if (Auth::check()) { diff --git a/app/Models/Tag.php b/app/Models/Tag.php index f9aa51a1a0..68868b0da4 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -170,6 +170,11 @@ class Tag extends Model } + /** + * @param Tag $value + * + * @return Tag + */ public static function routeBinder(Tag $value) { if (Auth::check()) { diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 168bfb9bf5..6a223428b4 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -44,6 +44,8 @@ class TransactionCurrency extends Model /** * @param TransactionCurrency $currency + * + * @return TransactionCurrency */ public static function routeBinder(TransactionCurrency $currency) { diff --git a/app/Repositories/Category/SingleCategoryRepository.php b/app/Repositories/Category/SingleCategoryRepository.php index 3b52f6c04b..95c41a26fa 100644 --- a/app/Repositories/Category/SingleCategoryRepository.php +++ b/app/Repositories/Category/SingleCategoryRepository.php @@ -25,7 +25,7 @@ class SingleCategoryRepository extends ComponentRepository implements SingleCate */ public function countJournals(Category $category) { - return $category->transactionJournals()->count(); + return $category->transactionjournals()->count(); } @@ -39,7 +39,7 @@ class SingleCategoryRepository extends ComponentRepository implements SingleCate */ public function countJournalsInRange(Category $category, Carbon $start, Carbon $end) { - return $category->transactionJournals()->before($end)->after($start)->count(); + return $category->transactionjournals()->before($end)->after($start)->count(); } /** diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 171468a6ac..4593d1d1fe 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -8,6 +8,11 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; use Illuminate\Support\Collection; +/** + * Class RuleGroupRepository + * + * @package FireflyIII\Repositories\RuleGroup + */ class RuleGroupRepository implements RuleGroupRepositoryInterface { /** diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index 060f5c5b17..f1dd3f317b 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -52,6 +52,8 @@ interface RuleGroupRepositoryInterface public function resetRuleGroupOrder(); /** + * @param RuleGroup $ruleGroup + * * @return bool */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup); diff --git a/app/Repositories/Shared/ComponentRepository.php b/app/Repositories/Shared/ComponentRepository.php index aa604b2859..391e78f21d 100644 --- a/app/Repositories/Shared/ComponentRepository.php +++ b/app/Repositories/Shared/ComponentRepository.php @@ -35,7 +35,7 @@ class ComponentRepository ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') ->whereIn('accounts.id', $ids) ->after($start) - ->first([DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`')]); + ->first([DB::raw('SUM(`transactions`.`amount`) as `journalAmount`')]); $amount = $entry->journalAmount; return $amount; diff --git a/app/Rules/Actions/ClearBudget.php b/app/Rules/Actions/ClearBudget.php index f74b633537..e1558afdcd 100644 --- a/app/Rules/Actions/ClearBudget.php +++ b/app/Rules/Actions/ClearBudget.php @@ -10,11 +10,8 @@ namespace FireflyIII\Rules\Actions; -use Auth; -use FireflyIII\Models\Category; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Log; /** * Class ClearBudget diff --git a/app/Rules/Actions/ClearCategory.php b/app/Rules/Actions/ClearCategory.php index 107f2241e0..76569523b8 100644 --- a/app/Rules/Actions/ClearCategory.php +++ b/app/Rules/Actions/ClearCategory.php @@ -10,11 +10,8 @@ namespace FireflyIII\Rules\Actions; -use Auth; -use FireflyIII\Models\Category; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Log; /** * Class ClearCategory diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index 45692c8603..49c7bd202b 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -39,6 +39,9 @@ class Processor /** * Processor constructor. + * + * @param Rule $rule + * @param TransactionJournal $journal */ public function __construct(Rule $rule, TransactionJournal $journal) { diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 6298bc61f6..00a13f85bd 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -136,7 +136,7 @@ class Amount { $currency = $transaction->transactionJournal->transactionCurrency; - return $this->formatAnything($currency, $transaction->amount); + return $this->formatAnything($currency, $transaction->amount, $coloured); } /** diff --git a/app/Support/Binder/CategoryList.php b/app/Support/Binder/CategoryList.php index ff787b85b5..4f39ca6e2f 100644 --- a/app/Support/Binder/CategoryList.php +++ b/app/Support/Binder/CategoryList.php @@ -19,7 +19,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @package FireflyIII\Support\Binder */ class CategoryList implements BinderInterface - { /** diff --git a/app/Support/Binder/Date.php b/app/Support/Binder/Date.php index 2aa4fe26de..db75e43d18 100644 --- a/app/Support/Binder/Date.php +++ b/app/Support/Binder/Date.php @@ -9,6 +9,7 @@ namespace FireflyIII\Support\Binder; +use Auth; use Carbon\Carbon; use Exception; use Log; diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 216d5528e4..647b75f138 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -28,7 +28,7 @@ class Steam $set = Auth::user()->transactions() ->whereIn('account_id', $accounts) ->groupBy('account_id') - ->get(['transactions.account_id', DB::Raw('MAX(`transaction_journals`.`date`) as `max_date`')]); + ->get(['transactions.account_id', DB::raw('MAX(`transaction_journals`.`date`) as `max_date`')]); foreach ($set as $entry) { $list[intval($entry->account_id)] = new Carbon($entry->max_date); diff --git a/app/Support/Twig/Rule.php b/app/Support/Twig/Rule.php index 8cd9ef6779..6b0a9e181d 100644 --- a/app/Support/Twig/Rule.php +++ b/app/Support/Twig/Rule.php @@ -8,18 +8,18 @@ use Twig_SimpleFunction; /** * Class Rule + * * @package FireflyIII\Support\Twig */ class Rule extends Twig_Extension { - /** - * - */ - public function getFunctions() - { - $functions = []; - $functions[] = new Twig_SimpleFunction( + /** + * @return Twig_SimpleFunction + */ + public function allJournalTriggers() + { + return new Twig_SimpleFunction( 'allJournalTriggers', function () { return [ 'store-journal' => trans('firefly.rule_trigger_store_journal'), @@ -27,8 +27,14 @@ class Rule extends Twig_Extension ]; } ); + } - $functions[] = new Twig_SimpleFunction( + /** + * @return Twig_SimpleFunction + */ + public function allRuleTriggers() + { + return new Twig_SimpleFunction( 'allRuleTriggers', function () { $ruleTriggers = array_keys(Config::get('firefly.rule-triggers')); $possibleTriggers = []; @@ -44,7 +50,15 @@ class Rule extends Twig_Extension ); - $functions[] = new Twig_SimpleFunction('allRuleActions', function () { + } + + /** + * @return Twig_SimpleFunction + */ + public function allActionTriggers() + { + return new Twig_SimpleFunction( + 'allRuleActions', function () { // array of valid values for actions $ruleActions = array_keys(Config::get('firefly.rule-actions')); $possibleActions = []; @@ -54,9 +68,21 @@ class Rule extends Twig_Extension unset($key, $ruleActions); return $possibleActions; - }); + } + ); + } + + /** + * @return array + */ + public function getFunctions() + { + return [ + $this->allJournalTriggers(), + $this->allRuleTriggers(), + $this->allActionTriggers(), + ]; - return $functions; } /** diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 6ca1067c65..728881a19d 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -40,12 +40,10 @@ class FireflyValidator extends Validator /** * @param $attribute - * @param $value - * @param $parameters * * @return bool */ - public function validateRuleTriggerValue($attribute, $value, $parameters) + public function validateRuleTriggerValue($attribute) { // get the index from a string like "rule-trigger-value.2". $parts = explode('.', $attribute); @@ -76,12 +74,10 @@ class FireflyValidator extends Validator /** * @param $attribute - * @param $value - * @param $parameters * * @return bool */ - public function validateRuleActionValue($attribute, $value, $parameters) + public function validateRuleActionValue($attribute) { // get the index from a string like "rule-action-value.2". $parts = explode('.', $attribute); From f69be86c743a0afc5006aaaca4fda492b359d560 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 17:53:54 +0100 Subject: [PATCH 167/276] Some code cleanup courtesy of PHPStorm. --- .../Account/ChartJsAccountChartGenerator.php | 11 +++----- app/Helpers/Csv/Wizard.php | 3 ++- app/Http/Controllers/Auth/AuthController.php | 14 +---------- .../Controllers/Chart/CategoryController.php | 20 +++------------ app/Http/Controllers/CsvController.php | 8 +++--- app/Http/Controllers/RuleController.php | 18 ++++--------- app/Http/Controllers/RuleGroupController.php | 2 +- .../Controllers/TransactionController.php | 6 ++--- app/Providers/FireflyServiceProvider.php | 1 - .../Category/CategoryRepository.php | 25 +++++-------------- app/Rules/Processor.php | 4 +-- app/Rules/Triggers/FromAccountEnds.php | 20 +++++++-------- 12 files changed, 39 insertions(+), 93 deletions(-) diff --git a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php index 4e446f617b..ef53c4b7bd 100644 --- a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php +++ b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php @@ -89,18 +89,13 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator { // language: $format = trans('config.month_and_day'); - $data = [ - 'count' => 0, - 'labels' => [], - 'datasets' => [], - ]; + $data = ['count' => 0, 'labels' => [], 'datasets' => [],]; $current = clone $start; while ($current <= $end) { $data['labels'][] = $current->formatLocalized($format); $current->addDay(); } - foreach ($accounts as $account) { $set = [ 'label' => $account->name, @@ -148,8 +143,8 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator 'datasets' => [ [ 'label' => $account->name, - 'data' => [] - ] + 'data' => [], + ], ], ]; $range = Steam::balanceInRange($account, $start, $end); diff --git a/app/Helpers/Csv/Wizard.php b/app/Helpers/Csv/Wizard.php index 148934ee60..03ed929ce0 100644 --- a/app/Helpers/Csv/Wizard.php +++ b/app/Helpers/Csv/Wizard.php @@ -63,7 +63,8 @@ class Wizard implements WizardInterface if (is_array($map)) { - foreach ($map as $index => $field) { + $keys = array_keys($map); + foreach ($keys as $index) { if (isset($roles[$index])) { $name = $roles[$index]; if ($configRoles[$name]['mappable']) { diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 6a634369b8..c7ec06cfcd 100755 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -66,15 +66,7 @@ class AuthController extends Controller */ public function login(Request $request) { - $this->validate( - $request, [ - $this->loginUsername() => 'required', 'password' => 'required', - ] - ); - - // If the class is using the ThrottlesLogins trait, we can automatically throttle - // the login attempts for this application. We'll key this by the username and - // the IP address of the client making these requests into this application. + $this->validate($request, [$this->loginUsername() => 'required', 'password' => 'required',]); $throttles = $this->isUsingThrottlesLoginsTrait(); if ($throttles && $this->hasTooManyLoginAttempts($request)) { @@ -102,10 +94,6 @@ class AuthController extends Controller $message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]); } - - // If the login attempt was unsuccessful we will increment the number of attempts - // to login and redirect the user back to the login form. Of course, when this - // user surpasses their maximum number of attempts they will get locked out. if ($throttles) { $this->incrementLoginAttempts($request); } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 028174d80e..eb91f28e00 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -43,6 +43,8 @@ class CategoryController extends Controller * @param SCRI $repository * @param Category $category * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * * @return \Symfony\Component\HttpFoundation\Response */ public function all(SCRI $repository, Category $category) @@ -67,7 +69,6 @@ class CategoryController extends Controller $spentArray = $repository->spentPerDay($category, $start, $end); $earnedArray = $repository->earnedPerDay($category, $start, $end); - while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); @@ -161,23 +162,8 @@ class CategoryController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - /** - * category - * year: - * spent: x - * earned: x - * year - * spent: x - * earned: x - */ $entries = new Collection; - // go by category, not by year. - - // given a set of categories and accounts, it should not be difficult to get - // the exact array of data we need. - - // then get the data for "no category". - $set = $repository->listMultiYear($categories, $accounts, $start, $end); + $set = $repository->listMultiYear($categories, $accounts, $start, $end); /** @var Category $category */ foreach ($categories as $category) { diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index 9d71d394ad..0006d6cfa5 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -79,8 +79,8 @@ class CsvController extends Controller if ($this->data->hasHeaders()) { $headers = $firstRow; } - - foreach (Config::get('csv.roles') as $name => $role) { + $keys = array_keys(Config::get('csv.roles')); + foreach ($keys as $name) { $availableRoles[$name] = trans('firefly.csv_column_' . $name);//$role['name']; } ksort($availableRoles); @@ -105,7 +105,7 @@ class CsvController extends Controller } $data = [ 'date-format' => Session::get('csv-date-format'), - 'has-headers' => Session::get('csv-has-headers') + 'has-headers' => Session::get('csv-has-headers'), ]; if (Session::has('csv-map')) { $data['map'] = Session::get('csv-map'); @@ -174,7 +174,7 @@ class CsvController extends Controller $delimiters = [ ',' => trans('form.csv_comma'), ';' => trans('form.csv_semicolon'), - 'tab' => trans('form.csv_tab') + 'tab' => trans('form.csv_tab'), ]; // get a list of asset accounts: diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 41489928db..9777a8f74a 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -134,28 +134,20 @@ class RuleController extends Controller { // has old input? if (Input::old()) { - // process old triggers. $oldTriggers = $this->getPreviousTriggers(); $triggerCount = count($oldTriggers); - - // process old actions - $oldActions = $this->getPreviousActions(); - $actionCount = count($oldActions); + $oldActions = $this->getPreviousActions(); + $actionCount = count($oldActions); } else { - // get current triggers $oldTriggers = $this->getCurrentTriggers($rule); $triggerCount = count($oldTriggers); - - // get current actions - $oldActions = $this->getCurrentActions($rule); - $actionCount = count($oldActions); + $oldActions = $this->getCurrentActions($rule); + $actionCount = count($oldActions); } // get rule trigger for update / store-journal: $primaryTrigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first()->trigger_value; - - - $subTitle = trans('firefly.edit_rule', ['title' => $rule->title]); + $subTitle = trans('firefly.edit_rule', ['title' => $rule->title]); // put previous url in session if not redirect from store (not "return_to_edit"). if (Session::get('rules.rule.edit.fromUpdate') !== true) { diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 7c375cd027..e0dda063ea 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -46,7 +46,7 @@ class RuleGroupController extends Controller Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'create-rule-group'); - return view('rules.rule-group.create', compact('subTitleIcon', 'what', 'subTitle')); + return view('rules.rule-group.create', compact('subTitleIcon', 'subTitle')); } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index a5009e0cca..50760bd6dc 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -58,9 +58,7 @@ class TransactionController extends Controller $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets[0] = trans('form.noBudget'); - - // piggy bank list: - $piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get(); + $piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get(); /** @var PiggyBank $piggy */ foreach ($piggyBanks as $piggy) { $piggy->name = $piggy->name . ' (' . Amount::format($piggy->currentRelevantRep()->currentamount, false) . ')'; @@ -161,7 +159,7 @@ class TransactionController extends Controller 'date' => $journal->date->format('Y-m-d'), 'category' => '', 'budget_id' => 0, - 'piggy_bank_id' => 0 + 'piggy_bank_id' => 0, ]; // get tags: $preFilled['tags'] = join(',', $journal->tags->pluck('tag')->toArray()); diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 1a57d10806..279fd8f67d 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -54,7 +54,6 @@ class FireflyServiceProvider extends ServiceProvider public function register() { - $this->app->bind( 'preferences', function () { return new Preferences; diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 534188bace..6d0cc981d2 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -76,22 +76,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end) { - /* - * select categories.id, DATE_FORMAT(transaction_journals.date,"%Y") as dateFormatted, transaction_types.type, SUM(amount) as sum from categories -left join category_transaction_journal ON category_transaction_journal.category_id = categories.id -left join transaction_journals ON transaction_journals.id = category_transaction_journal.transaction_journal_id -left join transaction_types ON transaction_types.id = transaction_journals.transaction_type_id -left join transactions ON transactions.transaction_journal_id = transaction_journals.id - - -where -categories.user_id =1 -and transaction_types.type in ("Withdrawal","Deposit") -and transactions.account_id IN (2,4,6,10,11,610,725,879,1248) - -group by categories.id, transaction_types.type, dateFormatted - */ $set = Auth::user()->categories() ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'category_transaction_journal.transaction_journal_id') @@ -100,6 +85,8 @@ group by categories.id, transaction_types.type, dateFormatted ->whereIn('transaction_types.type', [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL]) ->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) ->whereIn('categories.id', $categories->pluck('id')->toArray()) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->groupBy('categories.id') ->groupBy('transaction_types.type') ->groupBy('dateFormatted') @@ -108,7 +95,7 @@ group by categories.id, transaction_types.type, dateFormatted 'categories.*', DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y") as `dateFormatted`'), 'transaction_types.type', - DB::Raw('SUM(`amount`) as `sum`') + DB::Raw('SUM(`amount`) as `sum`'), ] ); @@ -158,7 +145,7 @@ group by categories.id, transaction_types.type, dateFormatted [ 'categories.*', DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'), - DB::Raw('SUM(`t_dest`.`amount`) AS `earned`') + DB::Raw('SUM(`t_dest`.`amount`) AS `earned`'), ] ); @@ -212,7 +199,7 @@ group by categories.id, transaction_types.type, dateFormatted [ 'categories.*', DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'), - DB::Raw('SUM(`t_src`.`amount`) AS `spent`') + DB::Raw('SUM(`t_src`.`amount`) AS `spent`'), ] ); @@ -286,7 +273,7 @@ group by categories.id, transaction_types.type, dateFormatted $single = $query->first( [ - DB::Raw('SUM(`transactions`.`amount`) as `sum`') + DB::Raw('SUM(`transactions`.`amount`) as `sum`'), ] ); if (!is_null($single)) { diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index 49c7bd202b..eb932ca2ec 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -72,7 +72,7 @@ class Processor $foundTriggers = 0; $hitTriggers = 0; /** @var RuleTrigger $trigger */ - foreach ($this->rule->ruleTriggers()->orderBy('order', 'ASC')->get() as $index => $trigger) { + foreach ($this->rule->ruleTriggers()->orderBy('order', 'ASC')->get() as $trigger) { $foundTriggers++; $type = $trigger->trigger_type; @@ -110,7 +110,7 @@ class Processor * @var int $index * @var RuleAction $action */ - foreach ($this->rule->ruleActions()->orderBy('order', 'ASC')->get() as $index => $action) { + foreach ($this->rule->ruleActions()->orderBy('order', 'ASC')->get() as $action) { $type = $action->action_type; $class = $this->actionTypes[$type]; Log::debug('Action #' . $action->id . ' for rule #' . $action->rule_id . ' (' . $type . ')'); diff --git a/app/Rules/Triggers/FromAccountEnds.php b/app/Rules/Triggers/FromAccountEnds.php index b207ef6ddd..57f686a063 100644 --- a/app/Rules/Triggers/FromAccountEnds.php +++ b/app/Rules/Triggers/FromAccountEnds.php @@ -44,29 +44,29 @@ class FromAccountEnds implements TriggerInterface */ public function triggered() { - $fromAccountName = strtolower($this->journal->source_account->name); - $fromAccountNameLength = strlen($fromAccountName); - $search = strtolower($this->trigger->trigger_value); - $searchLength = strlen($search); + $name = strtolower($this->journal->source_account->name); + $nameLength = strlen($name); + $search = strtolower($this->trigger->trigger_value); + $searchLength = strlen($search); // if the string to search for is longer than the account name, // shorten the search string. - if ($searchLength > $fromAccountNameLength) { - Log::debug('Search string "' . $search . '" (' . $searchLength . ') is longer than "' . $fromAccountName . '" (' . $fromAccountNameLength . '). '); - $search = substr($search, ($fromAccountNameLength * -1)); + if ($searchLength > $nameLength) { + Log::debug('Search string "' . $search . '" (' . $searchLength . ') is longer than "' . $name . '" (' . $nameLength . '). '); + $search = substr($search, ($nameLength * -1)); $searchLength = strlen($search); Log::debug('Search string is now "' . $search . '" (' . $searchLength . ') instead.'); } - $part = substr($fromAccountName, $searchLength * -1); + $part = substr($name, $searchLength * -1); if ($part == $search) { - Log::debug('"' . $fromAccountName . '" ends with "' . $search . '". Return true.'); + Log::debug('"' . $name . '" ends with "' . $search . '". Return true.'); return true; } - Log::debug('"' . $fromAccountName . '" does not end with "' . $search . '". Return false.'); + Log::debug('"' . $name . '" does not end with "' . $search . '". Return false.'); return false; From dcbfe90cf710cf78a069240f01a39dde53c3adfd Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 18:21:59 +0100 Subject: [PATCH 168/276] Some code cleanup courtesy of PHPStorm. --- app/Console/Kernel.php | 2 ++ app/Exceptions/Handler.php | 6 ++--- .../Controllers/Chart/CategoryController.php | 17 +++--------- .../Controllers/Chart/ReportController.php | 2 ++ app/Http/Controllers/RuleController.php | 2 ++ app/Repositories/Tag/TagRepository.php | 2 ++ app/Support/Amount.php | 6 ++--- app/Validation/FireflyValidator.php | 27 +++++++++++++++++-- 8 files changed, 43 insertions(+), 21 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 9579812620..488b36c74f 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -32,6 +32,8 @@ class Kernel extends ConsoleKernel * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameters) */ protected function schedule(Schedule $schedule) { diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 7913d17568..8216857d24 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -34,13 +34,13 @@ class Handler extends ExceptionHandler * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. * - * @param \Exception $e + * @param Exception $exception * * @return void */ - public function report(Exception $e) + public function report(Exception $exception) { - parent::report($e); + parent::report($exception); } /** diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index eb91f28e00..199319e62b 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -55,8 +55,6 @@ class CategoryController extends Controller $start = Navigation::startOfPeriod($start, $range); $end = new Carbon; $entries = new Collection; - - // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); @@ -71,26 +69,19 @@ class CategoryController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); - - // get the sum from $spentArray and $earnedArray: - $spent = $this->getSumOfRange($start, $currentEnd, $spentArray); - $earned = $this->getSumOfRange($start, $currentEnd, $earnedArray); - - $date = Navigation::periodShow($start, $range); + $spent = $this->getSumOfRange($start, $currentEnd, $spentArray); + $earned = $this->getSumOfRange($start, $currentEnd, $earnedArray); + $date = Navigation::periodShow($start, $range); $entries->push([clone $start, $date, $spent, $earned]); $start = Navigation::addPeriod($start, $range, 0); } - // limit the set to the last 40: $entries = $entries->reverse(); $entries = $entries->slice(0, 48); $entries = $entries->reverse(); - - $data = $this->generator->all($entries); + $data = $this->generator->all($entries); $cache->store($data); return Response::json($data); - - } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 545fe46d7a..97bb03be11 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -41,6 +41,8 @@ class ReportController extends Controller * @param Carbon $end * @param Collection $accounts * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * * @return \Illuminate\Http\JsonResponse */ public function yearInOut(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts) diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 9777a8f74a..03dd0c17f3 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -337,6 +337,8 @@ class RuleController extends Controller /** * @param Rule $rule + * + * @return array */ private function getCurrentActions(Rule $rule) { diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index ccdb2d9dae..f13f5bcc92 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -27,6 +27,8 @@ class TagRepository implements TagRepositoryInterface * @param TransactionJournal $journal * @param Tag $tag * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5. + * * @return boolean */ public function connect(TransactionJournal $journal, Tag $tag) diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 00a13f85bd..72bb329ad7 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -29,9 +29,9 @@ class Amount */ public function formatAnything(TransactionCurrency $format, $amount, $coloured = true) { - $locale = setlocale(LC_MONETARY, 0); - $a = new NumberFormatter($locale, NumberFormatter::CURRENCY); - $result = $a->formatCurrency($amount, $format->code); + $locale = setlocale(LC_MONETARY, 0); + $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $result = $formatter->formatCurrency($amount, $format->code); if ($coloured === true) { if ($amount == 0) { diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 728881a19d..90e65fb36a 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -32,6 +32,8 @@ class FireflyValidator extends Validator * @param array $rules * @param array $messages * @param array $customAttributes + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) // inherited from Laravel. */ public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = []) { @@ -52,8 +54,8 @@ class FireflyValidator extends Validator // loop all rule-triggers. // check if rule-value matches the thing. if (is_array($this->data['rule-trigger'])) { - $name = isset($this->data['rule-trigger'][$index]) ? $this->data['rule-trigger'][$index] : 'invalid'; - $value = isset($this->data['rule-trigger-value'][$index]) ? $this->data['rule-trigger-value'][$index] : false; + $name = $this->getRuleTriggerName($index); + $value = $this->getRuleTriggerValue($index); switch ($name) { default: return true; @@ -406,5 +408,26 @@ class FireflyValidator extends Validator return true; } + + /** + * @param int $index + * + * @return string + */ + private function getRuleTriggerName($index) + { + return isset($this->data['rule-trigger'][$index]) ? $this->data['rule-trigger'][$index] : 'invalid'; + + } + + /** + * @param int $index + * + * @return string + */ + private function getRuleTriggerValue($index) + { + return isset($this->data['rule-trigger-value'][$index]) ? $this->data['rule-trigger-value'][$index] : ''; + } } From 4e3c59a2da9726481ebcfee8d7550f98285fb837 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 19:37:09 +0100 Subject: [PATCH 169/276] Some cleaning up courtesy of PHPStorm. --- .../Controllers/Chart/CategoryController.php | 14 +++--- .../Controllers/Chart/ReportController.php | 2 +- app/Http/Controllers/CsvController.php | 3 ++ app/Models/Category.php | 1 + .../Account/AccountRepository.php | 43 +++++++++---------- app/Repositories/Budget/BudgetRepository.php | 2 + app/Repositories/Tag/TagRepository.php | 2 + app/Validation/FireflyValidator.php | 10 ++--- 8 files changed, 40 insertions(+), 37 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 199319e62b..f2a0623118 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -380,6 +380,9 @@ class CategoryController extends Controller * @param Carbon $end * @param Collection $accounts * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) // need all parameters + * @SuppressWarnings(PHPMD.ExcessuveMethodLength) // need the length + * * @return \Illuminate\Http\JsonResponse */ public function spentInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) @@ -404,18 +407,15 @@ class CategoryController extends Controller $entries = new Collection; while ($start < $end) { // filter the set: - $row = [clone $start]; - // get possibly relevant entries from the big $set - $currentSet = $set->filter( + $row = [clone $start]; + $currentSet = $set->filter(// get possibly relevant entries from the big $set function (Category $category) use ($start) { return $category->dateFormatted == $start->format("Y-m"); } ); - // check for each category if its in the current set. /** @var Category $category */ - foreach ($categories as $category) { - // if its in there, use the value. - $entry = $currentSet->filter( + foreach ($categories as $category) {// check for each category if its in the current set. + $entry = $currentSet->filter(// if its in there, use the value. function (Category $cat) use ($category) { return ($cat->id == $category->id); } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 97bb03be11..3ffb128691 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -41,7 +41,7 @@ class ReportController extends Controller * @param Carbon $end * @param Collection $accounts * - * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) // cant avoid it. * * @return \Illuminate\Http\JsonResponse */ diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index 0006d6cfa5..f041562c15 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -375,6 +375,9 @@ class CsvController extends Controller * * STEP TWO * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // need the length. + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // its exactly 5, its ok + * * @param Request $request * * @return \Illuminate\Http\RedirectResponse diff --git a/app/Models/Category.php b/app/Models/Category.php index 50342d1785..037b32af0c 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -21,6 +21,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property boolean $encrypted * @property-read Collection|TransactionJournal[] $transactionjournals * @property-read User $user + * @property string $dateFormatted */ class Category extends Model { diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 0dd0573754..94d4ae59e4 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -111,7 +111,7 @@ class AccountRepository implements AccountRepositoryInterface 'accounts.*', 'ccType.data as ccType', 'accountRole.data as accountRole', - DB::Raw('SUM(`transactions`.`amount`) AS `balance`') + DB::Raw('SUM(`transactions`.`amount`) AS `balance`'), ] ); @@ -377,6 +377,8 @@ class AccountRepository implements AccountRepositoryInterface * @param Account $account * @param array $data * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // need the complexity. + * * @return Account */ public function update(Account $account, array $data) @@ -390,15 +392,10 @@ class AccountRepository implements AccountRepositoryInterface $this->updateMetadata($account, $data); $openingBalance = $this->openingBalanceTransaction($account); - - // if has openingbalance? if ($data['openingBalance'] != 0) { - // if opening balance, do an update: if ($openingBalance) { - // update existing opening balance. $this->updateInitialBalance($account, $openingBalance, $data); } else { - // create new opening balance. $type = $data['openingBalance'] < 0 ? 'expense' : 'revenue'; $opposingData = [ 'user' => $data['user'], @@ -480,7 +477,7 @@ class AccountRepository implements AccountRepositoryInterface [ 'account_id' => $account->id, 'name' => $field, - 'data' => $data[$field] + 'data' => $data[$field], ] ); $metaData->save(); @@ -509,7 +506,7 @@ class AccountRepository implements AccountRepositoryInterface 'description' => 'Initial balance for "' . $account->name . '"', 'completed' => true, 'date' => $data['openingBalanceDate'], - 'encrypted' => true + 'encrypted' => true, ] ); @@ -547,21 +544,21 @@ class AccountRepository implements AccountRepositoryInterface foreach ($validFields as $field) { $entry = $account->accountMeta()->where('name', $field)->first(); - // update if new data is present: - if ($entry && isset($data[$field])) { - $entry->data = $data[$field]; - $entry->save(); - } - // no entry but data present? - if (!$entry && isset($data[$field])) { - $metaData = new AccountMeta( - [ - 'account_id' => $account->id, - 'name' => $field, - 'data' => $data[$field] - ] - ); - $metaData->save(); + if (isset($data[$field])) { + // update if new data is present: + if (!is_null($entry)) { + $entry->data = $data[$field]; + $entry->save(); + } else { + $metaData = new AccountMeta( + [ + 'account_id' => $account->id, + 'name' => $field, + 'data' => $data[$field], + ] + ); + $metaData->save(); + } } } diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 75c9ca247f..70d9e71b4b 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -575,6 +575,8 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn * @param Carbon $start * @param Carbon $end * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // it's a query. + * * @return array */ public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index f13f5bcc92..410ea191de 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -365,6 +365,8 @@ class TagRepository implements TagRepositoryInterface * @param TransactionJournal $journal * @param Tag $tag * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's complex but nothing can be done. + * * @return bool */ protected function matchAll(TransactionJournal $journal, Tag $tag) diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 90e65fb36a..d386645292 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -381,17 +381,16 @@ class FireflyValidator extends Validator * @param $value * @param $parameters * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) // cant remove it + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // its as simple as I can get it. * * @return bool */ public function validateUniquePiggyBankForUser($attribute, $value, $parameters) { $exclude = isset($parameters[0]) ? $parameters[0] : null; - $query = DB::table('piggy_banks'); - $query->whereNull('piggy_banks.deleted_at'); - $query->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id'); - $query->where('accounts.user_id', Auth::user()->id); + $query = DB::table('piggy_banks')->whereNull('piggy_banks.deleted_at') + ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', Auth::user()->id); if (!is_null($exclude)) { $query->where('piggy_banks.id', '!=', $exclude); } @@ -406,7 +405,6 @@ class FireflyValidator extends Validator } return true; - } /** From 83b7c9aa32c8b7fb2f3625620fd8085c6d20bb3e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 19:45:34 +0100 Subject: [PATCH 170/276] Fixed a bug in editing an opening balance --- app/Http/Controllers/AccountController.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e018002bfc..306f6463e8 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -251,12 +251,10 @@ class AccountController extends Controller 'virtualBalance' => round($request->input('virtualBalance'), 2), 'openingBalance' => round($request->input('openingBalance'), 2), 'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')), - 'openingBalanceCurrency' => intval($request->input('balance_currency_id')), + 'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')), 'ccType' => $request->input('ccType'), 'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'), ]; - - $repository->update($account, $accountData); Session::flash('success', 'Account "' . $account->name . '" updated.'); From 3649b595df8d41c6297ce4143924142019ce48a7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 19:48:09 +0100 Subject: [PATCH 171/276] Will no longer recognise transactions of type "opening balance". --- app/Models/TransactionJournal.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index d060021302..44f5788406 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -70,7 +70,7 @@ class TransactionJournal extends Model 'description' => 'required|between:1,1024', 'completed' => 'required|boolean', 'date' => 'required|date', - 'encrypted' => 'required|boolean' + 'encrypted' => 'required|boolean', ]; /** @@ -522,7 +522,11 @@ class TransactionJournal extends Model public static function routeBinder($value, $route) { if (Auth::check()) { - $object = TransactionJournal::where('id', $value)->where('user_id', Auth::user()->id)->first(); + $validTypes = [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]; + $object = TransactionJournal::where('transaction_journals.id', $value) + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->whereIn('transaction_types.type', $validTypes) + ->where('user_id', Auth::user()->id)->first(['transaction_journals.*']); if ($object) { return $object; } From 31533b5ef847379d2aae4771d5f6c5e375ed3a3b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 19:48:35 +0100 Subject: [PATCH 172/276] No need to check for opening balance. --- app/Http/Controllers/TransactionController.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 50760bd6dc..156f8f9703 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -345,10 +345,6 @@ class TransactionController extends Controller public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att, TransactionJournal $journal) { - if ($journal->isOpeningBalance()) { - return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!'); - } - $journalData = $request->getJournalData(); $repository->update($journal, $journalData); From 51062bc80bb223138fea5cb67d8de27748c79c4d Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 20:48:06 +0100 Subject: [PATCH 173/276] Some cleaning up and suppressing. --- .../Controllers/Chart/CategoryController.php | 16 ++++++++-------- app/Http/Controllers/CsvController.php | 2 ++ app/Repositories/Journal/JournalRepository.php | 2 ++ app/Repositories/Tag/TagRepository.php | 8 ++++---- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index f2a0623118..27c97ab7c6 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -313,6 +313,10 @@ class CategoryController extends Controller * @param Carbon $end * @param Collection $accounts * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) // cant avoid it. + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5. + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // it's long but ok. + * * @return \Illuminate\Http\JsonResponse */ public function earnedInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) @@ -337,18 +341,15 @@ class CategoryController extends Controller $entries = new Collection; while ($start < $end) { // filter the set: - $row = [clone $start]; - // get possibly relevant entries from the big $set - $currentSet = $set->filter( + $row = [clone $start]; + $currentSet = $set->filter( // get possibly relevant entries from the big $set function (Category $category) use ($start) { return $category->dateFormatted == $start->format("Y-m"); } ); - // check for each category if its in the current set. /** @var Category $category */ - foreach ($categories as $category) { - // if its in there, use the value. - $entry = $currentSet->filter( + foreach ($categories as $category) { // check for each category if its in the current set. + $entry = $currentSet->filter( // if its in there, use the value. function (Category $cat) use ($category) { return ($cat->id == $category->id); } @@ -359,7 +360,6 @@ class CategoryController extends Controller $row[] = 0; } } - $entries->push($row); $start->addMonth(); } diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index f041562c15..6bc6a9b2e3 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -331,6 +331,8 @@ class CsvController extends Controller * * STEP SIX * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) it's 6, but it's allright. + * * @return \Illuminate\Http\RedirectResponse */ public function saveMapping() diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index c3d4d08c5e..10d439b4fa 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -323,6 +323,8 @@ class JournalRepository implements JournalRepositoryInterface * @param array $data * * @return array + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function storeAccounts(TransactionType $type, array $data) { diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 410ea191de..5406054c79 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -318,6 +318,8 @@ class TagRepository implements TagRepositoryInterface * @param TransactionJournal $journal * @param Tag $tag * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * * @return boolean */ protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag) @@ -332,13 +334,11 @@ class TagRepository implements TagRepositoryInterface $withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); $deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count(); - // advance payments cannot accept transfers: - if ($journal->transaction_type_id == $transfer->id) { + if ($journal->transaction_type_id == $transfer->id) { // advance payments cannot accept transfers: return false; } - // the first transaction to be attached to this - // tag is attached just like that: + // the first transaction to be attached to this tag is attached just like that: if ($withdrawals < 1 && $deposits < 1) { $journal->tags()->save($tag); $journal->save(); From ffdd37ddd5eb51d29ed9a2a9836b6be983f0d435 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 20:57:26 +0100 Subject: [PATCH 174/276] More code cleanup. --- app/Exceptions/Handler.php | 6 +++--- app/Http/Controllers/Chart/BudgetController.php | 2 ++ app/Models/TransactionJournal.php | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 8216857d24..9b449ff362 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -47,12 +47,12 @@ class Handler extends ExceptionHandler * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request - * @param \Exception $e + * @param \Exception $exception * * @return \Illuminate\Http\Response */ - public function render($request, Exception $e) + public function render($request, Exception $exception) { - return parent::render($request, $e); + return parent::render($request, $exception); } } diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 39d3c4dffa..3fc267d732 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -46,6 +46,8 @@ class BudgetController extends Controller * @param Collection $accounts * @param Collection $budgets * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) // need all parameters + * * @return \Illuminate\Http\JsonResponse */ public function multiYear(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 44f5788406..d0f028dbc3 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -514,12 +514,11 @@ class TransactionJournal extends Model /** * @param $value - * @param $route * * @return mixed * @throws NotFoundHttpException */ - public static function routeBinder($value, $route) + public static function routeBinder($value) { if (Auth::check()) { $validTypes = [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]; From fbc9720f7adf9cffc8bf59264eb3a0249401a6a0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 21:50:57 +0100 Subject: [PATCH 175/276] Clean up some report methods. --- app/Helpers/Report/ReportHelper.php | 264 ++++++++++++++++++---------- 1 file changed, 176 insertions(+), 88 deletions(-) diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 709a306b0d..9a99653cfc 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -19,9 +19,12 @@ use FireflyIII\Helpers\Collection\Income; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget as BudgetModel; +use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; /** @@ -35,16 +38,23 @@ class ReportHelper implements ReportHelperInterface /** @var ReportQueryInterface */ protected $query; + /** @var BudgetRepositoryInterface */ + protected $budgetRepository; + + /** @var TagRepositoryInterface */ + protected $tagRepository; + /** * @codeCoverageIgnore * * @param ReportQueryInterface $query * */ - public function __construct(ReportQueryInterface $query) + public function __construct(ReportQueryInterface $query, BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository) { - $this->query = $query; - + $this->query = $query; + $this->budgetRepository = $budgetRepository; + $this->tagRepository = $tagRepository; } /** @@ -329,105 +339,30 @@ class ReportHelper implements ReportHelperInterface */ public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) { - /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + // /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ + // $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); - /** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */ - $tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface'); + // /** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */ + // $tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface'); $balance = new Balance; // build a balance header: $header = new BalanceHeader; - $budgets = $repository->getBudgetsAndLimitsInRange($start, $end); - $spentData = $repository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end); + $budgets = $this->budgetRepository->getBudgetsAndLimitsInRange($start, $end); + $spentData = $this->budgetRepository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end); foreach ($accounts as $account) { $header->addAccount($account); } /** @var BudgetModel $budget */ foreach ($budgets as $budget) { - $line = new BalanceLine; - $line->setBudget($budget); - - // loop accounts: - foreach ($accounts as $account) { - $balanceEntry = new BalanceEntry; - $balanceEntry->setAccount($account); - - // get spent: - $entry = $spentData->filter( - function (TransactionJournal $model) use ($budget, $account) { - return $model->account_id == $account->id && $model->budget_id == $budget->id; - } - ); - $spent = 0; - if (!is_null($entry->first())) { - $spent = $entry->first()->spent; - } - $balanceEntry->setSpent($spent); - $line->addBalanceEntry($balanceEntry); - } - // add line to balance: - $balance->addBalanceLine($line); + $balance->addBalanceLine($this->createBalanceLine($budget, $accounts, $spentData)); } - // then a new line for without budget. - // and one for the tags: - // and one for "left unbalanced". - $empty = new BalanceLine; - $tags = new BalanceLine; - $diffLine = new BalanceLine; - $tagsLeft = $tagRepository->allCoveredByBalancingActs($accounts, $start, $end); - - $tags->setRole(BalanceLine::ROLE_TAGROLE); - $diffLine->setRole(BalanceLine::ROLE_DIFFROLE); - - foreach ($accounts as $account) { - $entry = $spentData->filter( - function (TransactionJournal $model) use ($account) { - return $model->account_id == $account->id && is_null($model->budget_id); - } - ); - $spent = 0; - if (!is_null($entry->first())) { - $spent = $entry->first()->spent; - } - $leftEntry = $tagsLeft->filter( - function (Tag $tag) use ($account) { - return $tag->account_id == $account->id; - } - ); - $left = 0; - if (!is_null($leftEntry->first())) { - $left = $leftEntry->first()->sum; - } - bcscale(2); - $diff = bcadd($spent, $left); - - // budget - $budgetEntry = new BalanceEntry; - $budgetEntry->setAccount($account); - $budgetEntry->setSpent($spent); - $empty->addBalanceEntry($budgetEntry); - - // balanced by tags - $tagEntry = new BalanceEntry; - $tagEntry->setAccount($account); - $tagEntry->setLeft($left); - $tags->addBalanceEntry($tagEntry); - - // difference: - $diffEntry = new BalanceEntry; - $diffEntry->setAccount($account); - $diffEntry->setSpent($diff); - $diffLine->addBalanceEntry($diffEntry); - - } - - $balance->addBalanceLine($empty); - $balance->addBalanceLine($tags); - $balance->addBalanceLine($diffLine); + $balance->addBalanceLine($this->createEmptyBalanceLine($accounts, $spentData)); + $balance->addBalanceLine($this->createTagsBalanceLine($accounts, $start, $end)); + $balance->addBalanceLine($this->createDifferenceBalanceLine($accounts, $spentData, $start, $end)); $balance->setBalanceHeader($header); @@ -511,4 +446,157 @@ class ReportHelper implements ReportHelperInterface return $sum; } + + /** + * @param Budget $budget + * @param Collection $accounts + * @param Collection $spentData + * + * @return BalanceLine + */ + private function createBalanceLine(BudgetModel $budget, Collection $accounts, Collection $spentData) + { + $line = new BalanceLine; + $line->setBudget($budget); + + // loop accounts: + foreach ($accounts as $account) { + $balanceEntry = new BalanceEntry; + $balanceEntry->setAccount($account); + + // get spent: + $entry = $spentData->filter( + function (TransactionJournal $model) use ($budget, $account) { + return $model->account_id == $account->id && $model->budget_id == $budget->id; + } + ); + $spent = 0; + if (!is_null($entry->first())) { + $spent = $entry->first()->spent; + } + $balanceEntry->setSpent($spent); + $line->addBalanceEntry($balanceEntry); + } + + return $line; + } + + /** + * @param Collection $accounts + * @param Collection $spentData + * + * @return BalanceLine + */ + private function createEmptyBalanceLine(Collection $accounts, Collection $spentData) + { + $empty = new BalanceLine; + + foreach ($accounts as $account) { + $entry = $spentData->filter( + function (TransactionJournal $model) use ($account) { + return $model->account_id == $account->id && is_null($model->budget_id); + } + ); + $spent = 0; + if (!is_null($entry->first())) { + $spent = $entry->first()->spent; + } + + // budget + $budgetEntry = new BalanceEntry; + $budgetEntry->setAccount($account); + $budgetEntry->setSpent($spent); + $empty->addBalanceEntry($budgetEntry); + + } + + return $empty; + } + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return BalanceLine + */ + private function createTagsBalanceLine(Collection $accounts, Carbon $start, Carbon $end) + { + $tags = new BalanceLine; + $tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end); + + $tags->setRole(BalanceLine::ROLE_TAGROLE); + + foreach ($accounts as $account) { + $leftEntry = $tagsLeft->filter( + function (Tag $tag) use ($account) { + return $tag->account_id == $account->id; + } + ); + $left = 0; + if (!is_null($leftEntry->first())) { + $left = $leftEntry->first()->sum; + } + bcscale(2); + + // balanced by tags + $tagEntry = new BalanceEntry; + $tagEntry->setAccount($account); + $tagEntry->setLeft($left); + $tags->addBalanceEntry($tagEntry); + + } + + return $tags; + } + + /** + * @param Collection $accounts + * @param Collection $spentData + * @param Carbon $start + * @param Carbon $end + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * + * @return BalanceLine + */ + private function createDifferenceBalanceLine(Collection $accounts, Collection $spentData, Carbon $start, Carbon $end) + { + $diff = new BalanceLine; + $tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end); + + $diff->setRole(BalanceLine::ROLE_DIFFROLE); + + foreach ($accounts as $account) { + $entry = $spentData->filter( + function (TransactionJournal $model) use ($account) { + return $model->account_id == $account->id && is_null($model->budget_id); + } + ); + $spent = 0; + if (!is_null($entry->first())) { + $spent = $entry->first()->spent; + } + $leftEntry = $tagsLeft->filter( + function (Tag $tag) use ($account) { + return $tag->account_id == $account->id; + } + ); + $left = 0; + if (!is_null($leftEntry->first())) { + $left = $leftEntry->first()->sum; + } + bcscale(2); + $diffValue = bcadd($spent, $left); + + // difference: + $diffEntry = new BalanceEntry; + $diffEntry->setAccount($account); + $diffEntry->setSpent($diffValue); + $diff->addBalanceEntry($diffEntry); + + } + + return $diff; + } } From 7cb86add640c6df59a57479c4e1e77c133e97cf5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:13:38 +0100 Subject: [PATCH 176/276] New phpdoc. --- app/Helpers/Report/ReportHelper.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 9a99653cfc..813a2f34f4 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -18,8 +18,8 @@ use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; -use FireflyIII\Models\Budget as BudgetModel; use FireflyIII\Models\Budget; +use FireflyIII\Models\Budget as BudgetModel; use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; @@ -45,10 +45,13 @@ class ReportHelper implements ReportHelperInterface protected $tagRepository; /** + * ReportHelper constructor. + * * @codeCoverageIgnore * - * @param ReportQueryInterface $query - * + * @param ReportQueryInterface $query + * @param BudgetRepositoryInterface $budgetRepository + * @param TagRepositoryInterface $tagRepository */ public function __construct(ReportQueryInterface $query, BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository) { From 7ee25693aa58a989a71feb6f27dc327433a5e838 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:20:38 +0100 Subject: [PATCH 177/276] Removed stuff no longer used. --- app/Models/TransactionJournal.php | 101 +----------------------------- 1 file changed, 1 insertion(+), 100 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index d0f028dbc3..7187e13eeb 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -12,7 +12,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\TransactionJournal @@ -55,23 +54,12 @@ use Watson\Validating\ValidatingTrait; */ class TransactionJournal extends Model { - use SoftDeletes, ValidatingTrait; + use SoftDeletes; protected $fillable = ['user_id', 'transaction_type_id', 'bill_id', 'transaction_currency_id', 'description', 'completed', 'date', 'encrypted', 'tag_count']; protected $hidden = ['encrypted']; - protected $rules - = [ - 'user_id' => 'required|exists:users,id', - 'transaction_type_id' => 'required|exists:transaction_types,id', - 'bill_id' => 'exists:bills,id', - 'transaction_currency_id' => 'required|exists:transaction_currencies,id', - 'description' => 'required|between:1,1024', - 'completed' => 'required|boolean', - 'date' => 'required|date', - 'encrypted' => 'required|boolean', - ]; /** * @codeCoverageIgnore @@ -140,72 +128,6 @@ class TransactionJournal extends Model } - /** - * @param Tag $tag - * @param $amount - * - * @return string - */ - protected function amountByTagAdvancePayment(Tag $tag, $amount) - { - if ($this->isWithdrawal()) { - $others = $tag->transactionJournals()->transactionTypes([TransactionType::DEPOSIT])->get(); - foreach ($others as $other) { - $amount = bcsub($amount, $other->amount_positive); - } - - return $amount; - } - if ($this->isDeposit()) { - return '0'; - } - - return $amount; - } - - /** - * @param $tag - * @param $amount - * - * @return string - */ - protected function amountByTagBalancingAct($tag, $amount) - { - if ($this->isWithdrawal()) { - $transfer = $tag->transactionJournals()->transactionTypes([TransactionType::TRANSFER])->first(); - if ($transfer) { - $amount = bcsub($amount, $transfer->amount_positive); - - return $amount; - } - } - - return $amount; - } - - /** - * Assuming the journal has only one tag. Parameter amount is used as fallback. - * - * @param Tag $tag - * @param string $amount - * - * @return string - */ - protected function amountByTag(Tag $tag, $amount) - { - if ($tag->tagMode == 'advancePayment') { - return $this->amountByTagAdvancePayment($tag, $amount); - } - - if ($tag->tagMode == 'balancingAct') { - return $this->amountByTagBalancingAct($tag, $amount); - - } - - return $amount; - - } - /** * @codeCoverageIgnore * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany @@ -215,27 +137,6 @@ class TransactionJournal extends Model return $this->belongsToMany('FireflyIII\Models\Tag'); } - /** - * @param string $amount - * - * @return string - */ - public function amountByTags($amount) - { - $firstBalancingAct = $this->tags()->where('tagMode', 'balancingAct')->first(); - if ($firstBalancingAct) { - return $this->amountByTag($firstBalancingAct, $amount); - } - - $firstAdvancePayment = $this->tags()->where('tagMode', 'advancePayment')->first(); - if ($firstAdvancePayment) { - return $this->amountByTag($firstAdvancePayment, $amount); - } - - return $amount; - } - - /** * @codeCoverageIgnore * @return \Illuminate\Database\Eloquent\Relations\HasMany From 0620830b10b1301cdc752f93245791cd6c695309 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:23:01 +0100 Subject: [PATCH 178/276] Move from getDates to array dates. --- app/Models/TransactionJournal.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 7187e13eeb..ca160f5da5 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -60,6 +60,7 @@ class TransactionJournal extends Model protected $fillable = ['user_id', 'transaction_type_id', 'bill_id', 'transaction_currency_id', 'description', 'completed', 'date', 'encrypted', 'tag_count']; protected $hidden = ['encrypted']; + protected $dates = ['created_at', 'updated_at', 'date', 'deleted_at']; /** * @codeCoverageIgnore @@ -146,15 +147,6 @@ class TransactionJournal extends Model return $this->hasMany('FireflyIII\Models\Transaction'); } - /** - * @codeCoverageIgnore - * @return string[] - */ - public function getDates() - { - return ['created_at', 'updated_at', 'date', 'deleted_at']; - } - /** * Save the model to the database. * From f949d2476b255115df9ed7e6eb49830346a43b94 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:32:21 +0100 Subject: [PATCH 179/276] Move from getDates to array dates. --- app/Models/Account.php | 14 +++----------- app/Models/AccountMeta.php | 11 ++--------- app/Models/AccountType.php | 11 ++--------- app/Models/Bill.php | 14 +++----------- app/Models/Budget.php | 13 +++---------- app/Models/BudgetLimit.php | 9 +-------- app/Models/Category.php | 10 +--------- app/Models/Component.php | 21 ++------------------- app/Models/LimitRepetition.php | 9 +-------- app/Models/PiggyBank.php | 11 ++--------- app/Models/PiggyBankEvent.php | 10 +--------- app/Models/PiggyBankRepetition.php | 9 +-------- app/Models/Preference.php | 10 +--------- app/Models/Tag.php | 12 ++---------- app/Models/Transaction.php | 12 +++--------- app/Models/TransactionCurrency.php | 9 +-------- app/Models/TransactionGroup.php | 8 +------- app/Models/TransactionType.php | 8 +------- 18 files changed, 31 insertions(+), 170 deletions(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index 7545b3b9e1..ecfc6825ea 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -5,10 +5,10 @@ use Crypt; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\JoinClause; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\Account @@ -41,12 +41,13 @@ class Account extends Model protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban']; protected $hidden = ['virtual_balance_encrypted', 'encrypted']; + protected $dates = ['created_at', 'updated_at', 'deleted_at']; protected $rules = [ 'user_id' => 'required|exists:users,id', 'account_type_id' => 'required|exists:account_types,id', 'name' => 'required', - 'active' => 'required|boolean' + 'active' => 'required|boolean', ]; /** @@ -126,15 +127,6 @@ class Account extends Model return $this->belongsTo('FireflyIII\Models\AccountType'); } - /** - * @codeCoverageIgnore - * @return string[] - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } - /** * @codeCoverageIgnore * diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index aab45415f6..652f6a116e 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -24,9 +24,10 @@ class AccountMeta extends Model = [ 'account_id' => 'required|exists:accounts,id', 'name' => 'required|between:1,100', - 'data' => 'required' + 'data' => 'required', ]; protected $table = 'account_meta'; + protected $dates = ['created_at', 'updated_at']; /** * @@ -48,14 +49,6 @@ class AccountMeta extends Model return json_decode($value); } - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at']; - } - /** * @param $value */ diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 55f1e1f720..817d488b14 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -17,6 +17,8 @@ use Illuminate\Database\Eloquent\Model; class AccountType extends Model { + protected $dates = ['created_at', 'updated_at']; + // /** * @return \Illuminate\Database\Eloquent\Relations\HasMany @@ -25,13 +27,4 @@ class AccountType extends Model { return $this->hasMany('FireflyIII\Models\Account'); } - - /** - * @return array - */ - /** @noinspection PhpMissingParentCallCommonInspection */ - public function getDates() - { - return ['created_at', 'updated_at']; - } } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index 4558cdb4e7..d8ca9ef6dd 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -28,8 +28,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property boolean $match_encrypted * @property-read Collection|TransactionJournal[] $transactionjournals * @property-read User $user - * @property Carbon $nextExpectedMatch - * @property Carbon $lastFoundMatch + * @property Carbon $nextExpectedMatch + * @property Carbon $lastFoundMatch */ class Bill extends Model { @@ -38,15 +38,7 @@ class Bill extends Model = ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',]; protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted']; - - - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'date']; - } + protected $dates = ['created_at', 'updated_at', 'date']; /** * @param $value diff --git a/app/Models/Budget.php b/app/Models/Budget.php index a10bb8926d..ed72b0b1e8 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -23,8 +23,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read Collection|BudgetLimit[] $budgetlimits * @property-read Collection|TransactionJournal[] $transactionjournals * @property-read User $user - * @property string $dateFormatted - * @property string $budgeted + * @property string $dateFormatted + * @property string $budgeted */ class Budget extends Model { @@ -33,6 +33,7 @@ class Budget extends Model protected $fillable = ['user_id', 'name', 'active']; protected $hidden = ['encrypted']; + protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'enddate']; /** * @param array $fields @@ -71,14 +72,6 @@ class Budget extends Model return $this->hasMany('FireflyIII\Models\BudgetLimit'); } - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'enddate']; - } - /** * @param $value * diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index ba44ed72b0..da5c87501f 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -22,6 +22,7 @@ class BudgetLimit extends Model { protected $hidden = ['amount_encrypted']; + protected $date = ['created_at', 'updated_at', 'startdate']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -31,14 +32,6 @@ class BudgetLimit extends Model return $this->belongsTo('FireflyIII\Models\Budget'); } - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'startdate']; - } - /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ diff --git a/app/Models/Category.php b/app/Models/Category.php index 037b32af0c..84e79ae404 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -29,6 +29,7 @@ class Category extends Model protected $fillable = ['user_id', 'name']; protected $hidden = ['encrypted']; + protected $dates = ['created_at', 'updated_at', 'deleted_at']; /** * @param array $fields @@ -58,15 +59,6 @@ class Category extends Model } - /** - * @codeCoverageIgnore - * @return string[] - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } - /** * @codeCoverageIgnore * diff --git a/app/Models/Component.php b/app/Models/Component.php index 82ffc02f1c..1a950ad808 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -5,27 +5,10 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; /** - * FireflyIII\Models\Component + * Class Component * - * @property integer $id - * @property Carbon $created_at - * @property Carbon $updated_at - * @property Carbon $deleted_at - * @property string $name - * @property integer $user_id - * @property string $class + * @package FireflyIII\Models */ class Component extends Model { - use SoftDeletes; - - protected $fillable = ['user_id', 'name', 'class']; - - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } } diff --git a/app/Models/LimitRepetition.php b/app/Models/LimitRepetition.php index e4c3d105eb..bcb778f773 100644 --- a/app/Models/LimitRepetition.php +++ b/app/Models/LimitRepetition.php @@ -21,6 +21,7 @@ class LimitRepetition extends Model { protected $hidden = ['amount_encrypted']; + protected $dates = ['created_at', 'updated_at', 'startdate', 'enddate']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -30,14 +31,6 @@ class LimitRepetition extends Model return $this->belongsTo('FireflyIII\Models\BudgetLimit'); } - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'startdate', 'enddate']; - } - /** * @param $value */ diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index efce246557..73c1ab74db 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -27,7 +27,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read Account $account * @property-read Collection|PiggyBankRepetition[] $piggyBankRepetitions * @property-read Collection|PiggyBankEvent[] $piggyBankEvents - * @property string $reminder + * @property string $reminder */ class PiggyBank extends Model { @@ -36,6 +36,7 @@ class PiggyBank extends Model protected $fillable = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate', 'remind_me', 'reminder_skip']; protected $hidden = ['targetamount_encrypted', 'encrypted']; + protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -72,14 +73,6 @@ class PiggyBank extends Model return $this->hasMany('FireflyIII\Models\PiggyBankRepetition'); } - /** - * @return string[] - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate']; - } - /** * * @param $value diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index e373ab93a1..310b34ed09 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -21,15 +21,7 @@ class PiggyBankEvent extends Model protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'amount']; protected $hidden = ['amount_encrypted']; - - /** - * @return array - */ - /** @noinspection PhpMissingParentCallCommonInspection */ - public function getDates() - { - return ['created_at', 'updated_at', 'date']; - } + protected $dates = ['created_at', 'updated_at', 'date']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index 25ada5c27b..92b0b7564b 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -24,14 +24,7 @@ class PiggyBankRepetition extends Model protected $fillable = ['piggy_bank_id', 'startdate', 'targetdate', 'currentamount']; protected $hidden = ['currentamount_encrypted']; - - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'startdate', 'targetdate']; - } + protected $dates = ['created_at', 'updated_at', 'startdate', 'targetdate']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo diff --git a/app/Models/Preference.php b/app/Models/Preference.php index d66b36476e..625548d171 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -23,7 +23,7 @@ class Preference extends Model protected $fillable = ['user_id', 'data', 'name']; protected $hidden = ['data_encrypted', 'name_encrypted']; - +protected $dates = ['created_at', 'updated_at']; /** * @param $value * @@ -39,14 +39,6 @@ class Preference extends Model return json_decode($data); } - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at']; - } - /** * @param $value */ diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 68868b0da4..2fb076cc1d 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -41,8 +41,9 @@ class Tag extends Model 'date' => 'date', 'latitude' => 'numeric|min:-90|max:90', 'longitude' => 'numeric|min:-90|max:90', - 'tagMode' => 'required|in:nothing,balancingAct,advancePayment' + 'tagMode' => 'required|in:nothing,balancingAct,advancePayment', ]; + protected $dates = ['created_at', 'updated_at', 'date']; /** * @param array $fields @@ -76,15 +77,6 @@ class Tag extends Model } - /** - * @codeCoverageIgnore - * @return string[] - */ - public function getDates() - { - return ['created_at', 'updated_at', 'date']; - } - /** * Save the model to the database. * diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 1bdd1148a1..2357277d52 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -33,8 +33,10 @@ class Transaction extends Model 'account_id' => 'required|exists:accounts,id', 'transaction_journal_id' => 'required|exists:transaction_journals,id', 'description' => 'between:1,255', - 'amount' => 'required|numeric' + 'amount' => 'required|numeric', ]; + protected $dates = ['created_at', 'updated_at', 'deleted_at']; + use SoftDeletes, ValidatingTrait; /** @@ -55,14 +57,6 @@ class Transaction extends Model return $value; } - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } - /** * @param EloquentBuilder $query * @param Carbon $date diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 6a223428b4..1ed3691164 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -25,14 +25,7 @@ class TransactionCurrency extends Model protected $fillable = ['name', 'code', 'symbol']; - - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } + protected $dates = ['created_at', 'updated_at', 'deleted_at']; /** * @return \Illuminate\Database\Eloquent\Relations\HasMany diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index 5d2830778d..51275cf773 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -22,13 +22,7 @@ class TransactionGroup extends Model { use SoftDeletes; - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } + protected $dates = ['created_at', 'updated_at', 'deleted_at']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 6df752b214..e6778eb8cb 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -24,13 +24,7 @@ class TransactionType extends Model const TRANSFER = 'Transfer'; const OPENING_BALANCE = 'Opening balance'; - /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'deleted_at']; - } + protected $dates = ['created_at', 'updated_at', 'deleted_at']; /** * @return bool From 32ed8a4d8d1aae217ad0ceb75aaab3e14769bbab Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:41:26 +0100 Subject: [PATCH 180/276] Removed some unnecessary methods. --- app/Models/AccountMeta.php | 8 -------- app/Models/Tag.php | 12 ------------ app/Repositories/Account/AccountRepository.php | 3 ++- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index 652f6a116e..5b23dd2533 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -2,7 +2,6 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; -use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\AccountMeta @@ -18,14 +17,7 @@ use Watson\Validating\ValidatingTrait; class AccountMeta extends Model { - use ValidatingTrait; protected $fillable = ['account_id', 'name', 'data']; - protected $rules - = [ - 'account_id' => 'required|exists:accounts,id', - 'name' => 'required|between:1,100', - 'data' => 'required', - ]; protected $table = 'account_meta'; protected $dates = ['created_at', 'updated_at']; diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 2fb076cc1d..f9894ef980 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -9,7 +9,6 @@ use FireflyIII\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\Tag @@ -31,18 +30,7 @@ use Watson\Validating\ValidatingTrait; */ class Tag extends Model { - use ValidatingTrait; - protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode']; - protected $rules - = [ - 'tag' => 'required|min:1', - 'description' => 'min:1', - 'date' => 'date', - 'latitude' => 'numeric|min:-90|max:90', - 'longitude' => 'numeric|min:-90|max:90', - 'tagMode' => 'required|in:nothing,balancingAct,advancePayment', - ]; protected $dates = ['created_at', 'updated_at', 'date']; /** diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 94d4ae59e4..6a9ebf260b 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -323,7 +323,8 @@ class AccountRepository implements AccountRepositoryInterface { $journal = TransactionJournal ::orderBy('transaction_journals.date', 'ASC') - ->accountIs($account) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transactions.account_id', $account->id) ->transactionTypes([TransactionType::OPENING_BALANCE]) ->orderBy('created_at', 'ASC') ->first(['transaction_journals.*']); From b16149b8421e746e645a28180eb85bdb6ac2883c Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:44:17 +0100 Subject: [PATCH 181/276] Wrong array name. --- app/Models/BudgetLimit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index da5c87501f..1a05050db5 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -22,7 +22,7 @@ class BudgetLimit extends Model { protected $hidden = ['amount_encrypted']; - protected $date = ['created_at', 'updated_at', 'startdate']; + protected $dates = ['created_at', 'updated_at', 'startdate']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo From 3857e8d49f231dcbd1a730a2b4176032bdd1a901 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 22:44:25 +0100 Subject: [PATCH 182/276] Remove old code. --- app/Models/TransactionJournal.php | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index ca160f5da5..4299f900b1 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -207,21 +207,6 @@ class TransactionJournal extends Model return $this->hasMany('FireflyIII\Models\PiggyBankEvent'); } - /** - * @codeCoverageIgnore - * - * @param EloquentBuilder $query - * @param Account $account - */ - public function scopeAccountIs(EloquentBuilder $query, Account $account) - { - if (!isset($this->joinedTransactions)) { - $query->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'); - $this->joinedTransactions = true; - } - $query->where('transactions.account_id', $account->id); - } - /** * @codeCoverageIgnore * @@ -248,19 +233,6 @@ class TransactionJournal extends Model return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00')); } - /** - * @codeCoverageIgnore - * - * @param EloquentBuilder $query - * @param Carbon $date - * - * @return EloquentBuilder - */ - public function scopeOnDate(EloquentBuilder $query, Carbon $date) - { - return $query->where('date', '=', $date->format('Y-m-d')); - } - /** * @codeCoverageIgnore * From 7bf75128a86b2824a9ee919657431e37f40cc859 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jan 2016 23:12:52 +0100 Subject: [PATCH 183/276] Some cleaning up courtesy of PHPStorm. --- app/Console/Kernel.php | 8 +- .../Chart/Bill/ChartJsBillChartGenerator.php | 2 +- .../ChartJsCategoryChartGenerator.php | 10 +- .../ChartJsPiggyBankChartGenerator.php | 4 +- .../Report/ChartJsReportChartGenerator.php | 24 ++--- app/Handlers/Events/FireRulesForStore.php | 4 +- app/Handlers/Events/FireRulesForUpdate.php | 1 + app/Helpers/Csv/Converter/CategoryName.php | 2 +- app/Helpers/Csv/Data.php | 42 ++++---- app/Helpers/Report/ReportHelper.php | 2 +- app/Helpers/Report/ReportQuery.php | 4 +- app/Http/Controllers/AccountController.php | 2 +- app/Http/Controllers/Controller.php | 2 +- app/Http/Controllers/PiggyBankController.php | 2 +- app/Http/Controllers/ProfileController.php | 2 +- app/Http/Controllers/TagController.php | 2 +- app/Http/Middleware/Binder.php | 1 + app/Http/Middleware/EncryptCookies.php | 7 +- app/Http/Requests/AccountFormRequest.php | 2 +- app/Http/Requests/BudgetFormRequest.php | 2 +- app/Http/Requests/RuleFormRequest.php | 1 + app/Http/Requests/RuleGroupFormRequest.php | 2 +- app/Http/Requests/TagFormRequest.php | 2 +- app/Models/AccountMeta.php | 2 +- app/Models/BudgetLimit.php | 2 +- app/Models/Component.php | 2 - app/Models/PiggyBankEvent.php | 2 +- app/Models/PiggyBankRepetition.php | 2 +- app/Models/Preference.php | 3 +- app/Models/Rule.php | 1 + app/Models/RuleAction.php | 18 ++-- app/Models/RuleTrigger.php | 20 ++-- app/Providers/AuthServiceProvider.php | 10 +- app/Providers/EventServiceProvider.php | 6 +- app/Providers/FireflyServiceProvider.php | 2 +- app/Providers/RouteServiceProvider.php | 14 ++- app/Repositories/Bill/BillRepository.php | 4 +- app/Repositories/Budget/BudgetRepository.php | 10 +- .../Journal/JournalRepository.php | 4 +- app/Repositories/Tag/TagRepository.php | 2 +- app/Rules/Actions/ActionInterface.php | 3 +- app/Rules/Actions/SetBudget.php | 3 +- app/Support/Domain.php | 8 +- app/Support/Twig/Journal.php | 2 +- app/User.php | 98 +++++++++---------- resources/lang/en_US/firefly.php | 2 +- resources/lang/en_US/passwords.php | 2 +- resources/lang/fr_FR/firefly.php | 2 +- resources/lang/fr_FR/passwords.php | 2 +- resources/lang/nl_NL/firefly.php | 2 +- resources/lang/nl_NL/passwords.php | 2 +- resources/lang/pt_BR/firefly.php | 2 +- resources/lang/pt_BR/passwords.php | 2 +- resources/views/emails/registered-html.twig | 1 + resources/views/rules/partials/trigger.twig | 6 +- resources/views/rules/rule/delete.twig | 1 - 56 files changed, 193 insertions(+), 179 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 488b36c74f..4cc39c0df9 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -24,13 +24,15 @@ class Kernel extends ConsoleKernel * * @var array */ - protected $commands = [ - ]; + protected $commands + = [ + ]; /** * Define the application's command schedule. * - * @param \Illuminate\Console\Scheduling\Schedule $schedule + * @param \Illuminate\Console\Scheduling\Schedule $schedule + * * @return void * * @SuppressWarnings(PHPMD.UnusedFormalParameters) diff --git a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php index 879a1f8d84..ff1c9b6150 100644 --- a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php +++ b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php @@ -35,7 +35,7 @@ class ChartJsBillChartGenerator implements BillChartGenerator 'color' => 'rgba(0, 141, 76, 0.7)', 'highlight' => 'rgba(0, 141, 76, 0.9)', 'label' => trans('firefly.paid'), - ] + ], ]; return $data; diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index 8bb0402d25..07debb7581 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -28,12 +28,12 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator 'datasets' => [ [ 'label' => trans('firefly.spent'), - 'data' => [] + 'data' => [], ], [ 'label' => trans('firefly.earned'), - 'data' => [] - ] + 'data' => [], + ], ], ]; @@ -95,8 +95,8 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator 'datasets' => [ [ 'label' => trans('firefly.spent'), - 'data' => [] - ] + 'data' => [], + ], ], ]; foreach ($entries as $entry) { diff --git a/app/Generator/Chart/PiggyBank/ChartJsPiggyBankChartGenerator.php b/app/Generator/Chart/PiggyBank/ChartJsPiggyBankChartGenerator.php index bea4b9605a..a33b867911 100644 --- a/app/Generator/Chart/PiggyBank/ChartJsPiggyBankChartGenerator.php +++ b/app/Generator/Chart/PiggyBank/ChartJsPiggyBankChartGenerator.php @@ -31,8 +31,8 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator 'datasets' => [ [ 'label' => 'Diff', - 'data' => [] - ] + 'data' => [], + ], ], ]; $sum = '0'; diff --git a/app/Generator/Chart/Report/ChartJsReportChartGenerator.php b/app/Generator/Chart/Report/ChartJsReportChartGenerator.php index 1089ba7305..3b64f1a8b1 100644 --- a/app/Generator/Chart/Report/ChartJsReportChartGenerator.php +++ b/app/Generator/Chart/Report/ChartJsReportChartGenerator.php @@ -27,12 +27,12 @@ class ChartJsReportChartGenerator implements ReportChartGenerator 'datasets' => [ [ 'label' => trans('firefly.income'), - 'data' => [] + 'data' => [], ], [ 'label' => trans('firefly.expenses'), - 'data' => [] - ] + 'data' => [], + ], ], ]; @@ -60,12 +60,12 @@ class ChartJsReportChartGenerator implements ReportChartGenerator 'datasets' => [ [ 'label' => trans('firefly.income'), - 'data' => [] + 'data' => [], ], [ 'label' => trans('firefly.expenses'), - 'data' => [] - ] + 'data' => [], + ], ], ]; $data['datasets'][0]['data'][] = round($income, 2); @@ -92,12 +92,12 @@ class ChartJsReportChartGenerator implements ReportChartGenerator 'datasets' => [ [ 'label' => trans('firefly.income'), - 'data' => [] + 'data' => [], ], [ 'label' => trans('firefly.expenses'), - 'data' => [] - ] + 'data' => [], + ], ], ]; @@ -126,12 +126,12 @@ class ChartJsReportChartGenerator implements ReportChartGenerator 'datasets' => [ [ 'label' => trans('firefly.income'), - 'data' => [] + 'data' => [], ], [ 'label' => trans('firefly.expenses'), - 'data' => [] - ] + 'data' => [], + ], ], ]; $data['datasets'][0]['data'][] = round($income, 2); diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 1fa8554217..96853ad607 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -74,7 +74,7 @@ class FireRulesForStore } } -// echo 'Done processing rules. See log.'; -// exit; + // echo 'Done processing rules. See log.'; + // exit; } } \ No newline at end of file diff --git a/app/Handlers/Events/FireRulesForUpdate.php b/app/Handlers/Events/FireRulesForUpdate.php index d0b36c45dd..a88a097e85 100644 --- a/app/Handlers/Events/FireRulesForUpdate.php +++ b/app/Handlers/Events/FireRulesForUpdate.php @@ -8,6 +8,7 @@ */ namespace FireflyIII\Handlers\Events; + use FireflyIII\Events\TransactionJournalUpdated; use Log; diff --git a/app/Helpers/Csv/Converter/CategoryName.php b/app/Helpers/Csv/Converter/CategoryName.php index 52e371ad58..54fa5b72f7 100644 --- a/app/Helpers/Csv/Converter/CategoryName.php +++ b/app/Helpers/Csv/Converter/CategoryName.php @@ -24,7 +24,7 @@ class CategoryName extends BasicConverter implements ConverterInterface $category = Category::firstOrCreateEncrypted( [ 'name' => $this->value, - 'user_id' => Auth::user()->id + 'user_id' => Auth::user()->id, ] ); } diff --git a/app/Helpers/Csv/Data.php b/app/Helpers/Csv/Data.php index f0271091aa..2d6388792b 100644 --- a/app/Helpers/Csv/Data.php +++ b/app/Helpers/Csv/Data.php @@ -64,7 +64,7 @@ class Data protected function sessionHasHeaders() { if (Session::has('csv-has-headers')) { - $this->hasHeaders = (bool) Session::get('csv-has-headers'); + $this->hasHeaders = (bool)Session::get('csv-has-headers'); } } @@ -78,42 +78,42 @@ class Data protected function sessionDateFormat() { if (Session::has('csv-date-format')) { - $this->dateFormat = (string) Session::get('csv-date-format'); + $this->dateFormat = (string)Session::get('csv-date-format'); } } protected function sessionCsvFileLocation() { if (Session::has('csv-file')) { - $this->csvFileLocation = (string) Session::get('csv-file'); + $this->csvFileLocation = (string)Session::get('csv-file'); } } protected function sessionMap() { if (Session::has('csv-map')) { - $this->map = (array) Session::get('csv-map'); + $this->map = (array)Session::get('csv-map'); } } protected function sessionRoles() { if (Session::has('csv-roles')) { - $this->roles = (array) Session::get('csv-roles'); + $this->roles = (array)Session::get('csv-roles'); } } protected function sessionMapped() { if (Session::has('csv-mapped')) { - $this->mapped = (array) Session::get('csv-mapped'); + $this->mapped = (array)Session::get('csv-mapped'); } } protected function sessionSpecifix() { if (Session::has('csv-specifix')) { - $this->specifix = (array) Session::get('csv-specifix'); + $this->specifix = (array)Session::get('csv-specifix'); } } @@ -135,7 +135,7 @@ class Data /** * - * @param mixed $dateFormat + * @param mixed $dateFormat */ public function setDateFormat($dateFormat) { @@ -145,7 +145,7 @@ class Data /** * - * @param int $importAccount + * @param int $importAccount */ public function setImportAccount($importAccount) { @@ -164,7 +164,7 @@ class Data /** * - * @param bool $hasHeaders + * @param bool $hasHeaders */ public function setHasHeaders($hasHeaders) { @@ -183,7 +183,7 @@ class Data /** * - * @param array $map + * @param array $map */ public function setMap(array $map) { @@ -202,7 +202,7 @@ class Data /** * - * @param array $mapped + * @param array $mapped */ public function setMapped(array $mapped) { @@ -219,19 +219,19 @@ class Data if (strlen($this->csvFileContent) === 0) { $this->loadCsvFile(); } - + if (is_null($this->reader)) { $this->reader = Reader::createFromString($this->getCsvFileContent()); $this->reader->setDelimiter($this->delimiter); } - + return $this->reader; } protected function loadCsvFile() { - $file = $this->getCsvFileLocation(); - $content = file_get_contents($file); + $file = $this->getCsvFileLocation(); + $content = file_get_contents($file); $contentDecrypted = Crypt::decrypt($content); $this->setCsvFileContent($contentDecrypted); } @@ -247,7 +247,7 @@ class Data /** * - * @param string $csvFileLocation + * @param string $csvFileLocation */ public function setCsvFileLocation($csvFileLocation) { @@ -266,7 +266,7 @@ class Data /** * - * @param string $csvFileContent + * @param string $csvFileContent */ public function setCsvFileContent($csvFileContent) { @@ -284,7 +284,7 @@ class Data /** * - * @param array $roles + * @param array $roles */ public function setRoles(array $roles) { @@ -303,7 +303,7 @@ class Data /** * - * @param array $specifix + * @param array $specifix */ public function setSpecifix($specifix) { @@ -322,7 +322,7 @@ class Data /** * - * @param string $delimiter + * @param string $delimiter */ public function setDelimiter($delimiter) { diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 813a2f34f4..7daede4b45 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -18,8 +18,8 @@ use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; -use FireflyIII\Models\Budget; use FireflyIII\Models\Budget as BudgetModel; +use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; diff --git a/app/Helpers/Report/ReportQuery.php b/app/Helpers/Report/ReportQuery.php index 24fa89dc7d..0180571484 100644 --- a/app/Helpers/Report/ReportQuery.php +++ b/app/Helpers/Report/ReportQuery.php @@ -50,7 +50,7 @@ class ReportQuery implements ReportQueryInterface ->get( [ DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), - DB::Raw('SUM(`t_from`.`amount`) AS `sum`') + DB::Raw('SUM(`t_from`.`amount`) AS `sum`'), ] ); $array = []; @@ -95,7 +95,7 @@ class ReportQuery implements ReportQueryInterface ->get( [ DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), - DB::Raw('SUM(`t_to`.`amount`) AS `sum`') + DB::Raw('SUM(`t_to`.`amount`) AS `sum`'), ] ); $array = []; diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 306f6463e8..44d210ca40 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -133,7 +133,7 @@ class AccountController extends Controller 'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'), 'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null, 'openingBalance' => $openingBalanceAmount, - 'virtualBalance' => round($account->virtual_balance, 2) + 'virtualBalance' => round($account->virtual_balance, 2), ]; Session::flash('preFilled', $preFilled); Session::flash('gaEventCategory', 'accounts'); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 2d33717042..6241057961 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -57,7 +57,7 @@ class Controller extends BaseController $localeconv = [ 'mon_decimal_point' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL)), 'mon_thousands_sep' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL)), - 'frac_digits' => $numberFormatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS) + 'frac_digits' => $numberFormatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS), ]; View::share('monthFormat', $this->monthFormat); View::share('monthAndDayFormat', $this->monthAndDayFormat); diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 83ed844119..a3b93b97ac 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -186,7 +186,7 @@ class PiggyBankController extends Controller 'leftForPiggyBanks' => $repository->leftOnAccount($account, $end), 'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfTargets' => round($piggyBank->targetamount, 2), - 'leftToSave' => $piggyBank->leftToSave + 'leftToSave' => $piggyBank->leftToSave, ]; } else { $accounts[$account->id]['sumOfSaved'] = bcadd($accounts[$account->id]['sumOfSaved'], $piggyBank->savedSoFar); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 684d9e2d98..da581288e5 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -126,7 +126,7 @@ class ProfileController extends Controller 'email' => $email, 'password' => 'deleted', 'blocked' => 1, - 'blocked_code' => 'deleted' + 'blocked_code' => 'deleted', ] ); diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index bf0baa0805..bdd0c6bab5 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -60,7 +60,7 @@ class TagController extends Controller $subTitleIcon = 'fa-tag'; $preFilled = [ - 'tagMode' => 'nothing' + 'tagMode' => 'nothing', ]; if (!Input::old('tagMode')) { Session::flash('preFilled', $preFilled); diff --git a/app/Http/Middleware/Binder.php b/app/Http/Middleware/Binder.php index 088f1e9a8d..3a3ccb3c85 100644 --- a/app/Http/Middleware/Binder.php +++ b/app/Http/Middleware/Binder.php @@ -54,6 +54,7 @@ class Binder private function performBinding($key, $value, $route) { $class = $this->binders[$key]; + return $class::routeBinder($value, $route); } } diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index 2686edac4f..8784890ec9 100755 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -16,7 +16,8 @@ class EncryptCookies extends BaseEncrypter * * @var array */ - protected $except = [ - // - ]; + protected $except + = [ + // + ]; } diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 578c034176..004336e184 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -53,7 +53,7 @@ class AccountFormRequest extends Request 'ccMonthlyPaymentDate' => 'date', 'amount_currency_id_openingBalance' => 'exists:transaction_currencies,id', 'amount_currency_id_virtualBalance' => 'exists:transaction_currencies,id', - 'what' => 'in:' . $types + 'what' => 'in:' . $types, ]; } } diff --git a/app/Http/Requests/BudgetFormRequest.php b/app/Http/Requests/BudgetFormRequest.php index 11207e8037..8d99f5f52a 100644 --- a/app/Http/Requests/BudgetFormRequest.php +++ b/app/Http/Requests/BudgetFormRequest.php @@ -36,7 +36,7 @@ class BudgetFormRequest extends Request return [ 'name' => $nameRule, - 'active' => 'numeric|between:0,1' + 'active' => 'numeric|between:0,1', ]; } } diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index d5e7423b36..be60f0e06a 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -62,6 +62,7 @@ class RuleFormRequest extends Request for ($i = 0; $i < 10; $i++) { $rules['rule-action-value.' . $i] = 'required_if:rule-action.' . $i . ',' . $contextActions . '|ruleActionValue'; } + return $rules; } } diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index 638c50341b..112a1dcca6 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -42,7 +42,7 @@ class RuleGroupFormRequest extends Request } return [ - 'title' => $titleRule, + 'title' => $titleRule, 'description' => 'between:1,5000', ]; } diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index 28773c874c..a4f5da9021 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -42,7 +42,7 @@ class TagFormRequest extends Request 'latitude' => 'numeric|min:-90|max:90', 'longitude' => 'numeric|min:-90|max:90', 'zoomLevel' => 'numeric|min:0|max:80', - 'tagMode' => 'required|in:nothing,balancingAct,advancePayment' + 'tagMode' => 'required|in:nothing,balancingAct,advancePayment', ]; } } diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index 5b23dd2533..846ff8954b 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -17,9 +17,9 @@ use Illuminate\Database\Eloquent\Model; class AccountMeta extends Model { + protected $dates = ['created_at', 'updated_at']; protected $fillable = ['account_id', 'name', 'data']; protected $table = 'account_meta'; - protected $dates = ['created_at', 'updated_at']; /** * diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index 1a05050db5..0a05e24836 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -22,7 +22,7 @@ class BudgetLimit extends Model { protected $hidden = ['amount_encrypted']; - protected $dates = ['created_at', 'updated_at', 'startdate']; + protected $dates = ['created_at', 'updated_at', 'startdate']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo diff --git a/app/Models/Component.php b/app/Models/Component.php index 1a950ad808..c7edc8dca4 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -1,8 +1,6 @@ 'FireflyIII\Policies\ModelPolicy', - ]; + protected $policies + = [ + 'FireflyIII\Model' => 'FireflyIII\Policies\ModelPolicy', + ]; /** * Register any application authentication / authorization services. * - * @param \Illuminate\Contracts\Auth\Access\Gate $gate + * @param \Illuminate\Contracts\Auth\Access\Gate $gate + * * @return void */ public function boot(GateContract $gate) diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 7b86a54d04..7d0b742adf 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -29,19 +29,19 @@ class EventServiceProvider extends ServiceProvider */ protected $listen = [ - 'FireflyIII\Events\TransactionJournalUpdated' => [ + 'FireflyIII\Events\TransactionJournalUpdated' => [ 'FireflyIII\Handlers\Events\ScanForBillsAfterUpdate', 'FireflyIII\Handlers\Events\UpdateJournalConnection', 'FireflyIII\Handlers\Events\FireRulesForUpdate', ], - 'FireflyIII\Events\TransactionJournalStored' => [ + 'FireflyIII\Events\TransactionJournalStored' => [ 'FireflyIII\Handlers\Events\ScanForBillsAfterStore', 'FireflyIII\Handlers\Events\ConnectJournalToPiggyBank', 'FireflyIII\Handlers\Events\FireRulesForStore', - ] + ], ]; diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 279fd8f67d..ba3b700a19 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -11,8 +11,8 @@ use FireflyIII\Support\Twig\Budget; use FireflyIII\Support\Twig\General; use FireflyIII\Support\Twig\Journal; use FireflyIII\Support\Twig\PiggyBank; -use FireflyIII\Support\Twig\Translation; use FireflyIII\Support\Twig\Rule; +use FireflyIII\Support\Twig\Translation; use FireflyIII\Validation\FireflyValidator; use Illuminate\Support\ServiceProvider; use Twig; diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 34cdcf76c6..6e583159bf 100755 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,8 +2,8 @@ namespace FireflyIII\Providers; -use Illuminate\Routing\Router; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; +use Illuminate\Routing\Router; /** * Class RouteServiceProvider @@ -24,7 +24,8 @@ class RouteServiceProvider extends ServiceProvider /** * Define your route model bindings, pattern filters, etc. * - * @param \Illuminate\Routing\Router $router + * @param \Illuminate\Routing\Router $router + * * @return void */ public function boot(Router $router) @@ -37,13 +38,16 @@ class RouteServiceProvider extends ServiceProvider /** * Define the routes for the application. * - * @param \Illuminate\Routing\Router $router + * @param \Illuminate\Routing\Router $router + * * @return void */ public function map(Router $router) { - $router->group(['namespace' => $this->namespace], function ($router) { + $router->group( + ['namespace' => $this->namespace], function ($router) { require app_path('Http/routes.php'); - }); + } + ); } } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 17386d9797..29bd373877 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -61,7 +61,7 @@ class BillRepository implements BillRepositoryInterface ->get( [ 'transaction_journals.bill_id', - DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`') + DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`'), ] ); @@ -476,7 +476,7 @@ class BillRepository implements BillRepositoryInterface ->get( [ 'bills.*', - DB::Raw('(`bills`.`amount_min` + `bills`.`amount_max` / 2) as `expectedAmount`') + DB::Raw('(`bills`.`amount_min` + `bills`.`amount_max` / 2) as `expectedAmount`'), ] )->sortBy('name'); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 70d9e71b4b..715665e372 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -89,7 +89,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn ->get( [ DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'), - DB::Raw('SUM(`transactions`.`amount`) as `monthlyAmount`') + DB::Raw('SUM(`transactions`.`amount`) as `monthlyAmount`'), ] ); @@ -316,7 +316,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn [ 'budgets.*', DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'), - DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`') + DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`'), ] ); @@ -559,7 +559,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn [ 'budgets.*', DB::Raw('DATE_FORMAT(`limit_repetitions`.`startdate`,"%Y") as `dateFormatted`'), - DB::Raw('SUM(`limit_repetitions`.`amount`) as `budgeted`') + DB::Raw('SUM(`limit_repetitions`.`amount`) as `budgeted`'), ] ); @@ -603,7 +603,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn [ 'budgets.*', DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y") AS `dateFormatted`'), - DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`') + DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`'), ] ); @@ -720,7 +720,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn ->get( [ 't_from.account_id', 'budget_transaction_journal.budget_id', - DB::Raw('SUM(`t_from`.`amount`) AS `spent`') + DB::Raw('SUM(`t_from`.`amount`) AS `spent`'), ] ); diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 10d439b4fa..e0b3dc3dc8 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -196,14 +196,14 @@ class JournalRepository implements JournalRepositoryInterface [ 'account_id' => $fromAccount->id, 'transaction_journal_id' => $journal->id, - 'amount' => $data['amount'] * -1 + 'amount' => $data['amount'] * -1, ] ); Transaction::create( // second transaction. [ 'account_id' => $toAccount->id, 'transaction_journal_id' => $journal->id, - 'amount' => $data['amount'] + 'amount' => $data['amount'], ] ); $journal->completed = 1; diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 5406054c79..d0368d05f1 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -131,7 +131,7 @@ class TagRepository implements TagRepositoryInterface ->get( [ 't_to.account_id', - DB::Raw('SUM(`t_to`.`amount`) as `sum`') + DB::Raw('SUM(`t_to`.`amount`) as `sum`'), ] ); diff --git a/app/Rules/Actions/ActionInterface.php b/app/Rules/Actions/ActionInterface.php index 16a597b7e1..91cbec46c7 100644 --- a/app/Rules/Actions/ActionInterface.php +++ b/app/Rules/Actions/ActionInterface.php @@ -8,6 +8,7 @@ */ namespace FireflyIII\Rules\Actions; + use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; @@ -21,7 +22,7 @@ interface ActionInterface /** * TriggerInterface constructor. * - * @param RuleAction $action + * @param RuleAction $action * @param TransactionJournal $journal */ public function __construct(RuleAction $action, TransactionJournal $journal); diff --git a/app/Rules/Actions/SetBudget.php b/app/Rules/Actions/SetBudget.php index 5f77b75595..5373fdd6d4 100644 --- a/app/Rules/Actions/SetBudget.php +++ b/app/Rules/Actions/SetBudget.php @@ -15,6 +15,7 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Log; + /** * Class SetBudget * @@ -56,7 +57,7 @@ class SetBudget implements ActionInterface Log::debug('Will set budget "' . $search . '" (#' . $budget->id . ') on journal #' . $this->journal->id . '.'); $this->journal->budgets()->save($budget); } else { - Log::debug('Could not find budget "'.$search.'". Failed.'); + Log::debug('Could not find budget "' . $search . '". Failed.'); } return true; diff --git a/app/Support/Domain.php b/app/Support/Domain.php index d1f8be1fed..8506fcc998 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -30,16 +30,16 @@ class Domain /** * @return array */ - public static function getRuleTriggers() + public static function getRuleActions() { - return Config::get('firefly.rule-triggers'); + return Config::get('firefly.rule-actions'); } /** * @return array */ - public static function getRuleActions() + public static function getRuleTriggers() { - return Config::get('firefly.rule-actions'); + return Config::get('firefly.rule-triggers'); } } \ No newline at end of file diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index 628745d1f8..29b9c2d31f 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -37,7 +37,7 @@ class Journal extends Twig_Extension { $functions = [ $this->invalidJournal(), - $this->relevantTags() + $this->relevantTags(), ]; return $functions; diff --git a/app/User.php b/app/User.php index 1fb85eb23f..b274e6d9dc 100755 --- a/app/User.php +++ b/app/User.php @@ -9,24 +9,24 @@ use Zizaco\Entrust\Traits\EntrustUserTrait; * Class User * * @package FireflyIII - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $email - * @property string $password - * @property string $remember_token - * @property string $reset - * @property boolean $blocked - * @property string $blocked_code - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Account[] $accounts - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Tag[] $tags - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Bill[] $bills - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Budget[] $budgets - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Category[] $categories - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Preference[] $preferences + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $email + * @property string $password + * @property string $remember_token + * @property string $reset + * @property boolean $blocked + * @property string $blocked_code + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Account[] $accounts + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Tag[] $tags + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Bill[] $bills + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Budget[] $budgets + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Category[] $categories + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Preference[] $preferences * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionjournals - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Role[] $roles + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Role[] $roles */ class User extends Authenticatable { @@ -71,30 +71,6 @@ class User extends Authenticatable return $this->hasMany('FireflyIII\Models\Attachment'); } - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function tags() - { - return $this->hasMany('FireflyIII\Models\Tag'); - } - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function rules() - { - return $this->hasMany('FireflyIII\Models\Rule'); - } - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function ruleGroups() - { - return $this->hasMany('FireflyIII\Models\RuleGroup'); - } - /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -127,14 +103,6 @@ class User extends Authenticatable return $this->hasManyThrough('FireflyIII\Models\PiggyBank', 'FireflyIII\Models\Account'); } - /** - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough - */ - public function transactions() - { - return $this->hasManyThrough('FireflyIII\Models\Transaction', 'FireflyIII\Models\TransactionJournal'); - } - /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -143,6 +111,30 @@ class User extends Authenticatable return $this->hasMany('FireflyIII\Models\Preference'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function ruleGroups() + { + return $this->hasMany('FireflyIII\Models\RuleGroup'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function rules() + { + return $this->hasMany('FireflyIII\Models\Rule'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function tags() + { + return $this->hasMany('FireflyIII\Models\Tag'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -151,4 +143,12 @@ class User extends Authenticatable return $this->hasMany('FireflyIII\Models\TransactionJournal'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough + */ + public function transactions() + { + return $this->hasManyThrough('FireflyIII\Models\Transaction', 'FireflyIII\Models\TransactionJournal'); + } + } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index fa7ef1932c..be816a7d26 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -282,7 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', // create new stuff: diff --git a/resources/lang/en_US/passwords.php b/resources/lang/en_US/passwords.php index f248128198..125f093fa8 100644 --- a/resources/lang/en_US/passwords.php +++ b/resources/lang/en_US/passwords.php @@ -17,6 +17,6 @@ return [ "token" => "This password reset token is invalid.", "sent" => "We have e-mailed your password reset link!", "reset" => "Your password has been reset!", - 'blocked' => 'Nice try though.' + 'blocked' => 'Nice try though.', ]; diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index 3508e76839..b0a5e55779 100755 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -282,7 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', // create new stuff: diff --git a/resources/lang/fr_FR/passwords.php b/resources/lang/fr_FR/passwords.php index 81b522829e..5b2f00ff3a 100755 --- a/resources/lang/fr_FR/passwords.php +++ b/resources/lang/fr_FR/passwords.php @@ -17,6 +17,6 @@ return [ "token" => "Le jeton de réinitialisation de mot de passe est invalide.", "sent" => "Nous avons envoyé votre lien de réinitialisation de mot de passe!", "reset" => "Le mot de passe a été réinitialisé!", - 'blocked' => 'Nice try though.' + 'blocked' => 'Nice try though.', ]; diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 73fe1865c3..3493ef0643 100755 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -282,7 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', - 'csv_delimiter_help' => 'Kies het veldscheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', + 'csv_delimiter_help' => 'Kies het veldscheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', 'csv_date_parse_error' => 'Firefly kan van ":value" geen datum maken, gegeven het formaat ":format". Weet je zeker dat je CSV goed is?', // create new stuff: diff --git a/resources/lang/nl_NL/passwords.php b/resources/lang/nl_NL/passwords.php index 6b189b754b..fc84b44aa5 100755 --- a/resources/lang/nl_NL/passwords.php +++ b/resources/lang/nl_NL/passwords.php @@ -17,6 +17,6 @@ return [ "token" => "Ongeldig token! Sorry", "sent" => "Je krijgt een mailtje met een linkje om je wachtwoord te herstellen!", "reset" => "Je wachtwoord is hersteld!", - 'blocked' => 'Leuk geprobeerd wel.' + 'blocked' => 'Leuk geprobeerd wel.', ]; diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php index 0ca7a858b5..fedd465573 100755 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -282,7 +282,7 @@ return [ 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', // create new stuff: diff --git a/resources/lang/pt_BR/passwords.php b/resources/lang/pt_BR/passwords.php index 7e45ef4749..914544dfb5 100755 --- a/resources/lang/pt_BR/passwords.php +++ b/resources/lang/pt_BR/passwords.php @@ -17,6 +17,6 @@ return [ "token" => "Este token de redefinição de senha é inválido.", "sent" => "Nós te enviamos um email com um link para trocar a senha!", "reset" => "Sua senha foi redefinida!", - 'blocked' => 'Nice try though.' + 'blocked' => 'Nice try though.', ]; diff --git a/resources/views/emails/registered-html.twig b/resources/views/emails/registered-html.twig index 532322e75a..5c89c1a81d 100644 --- a/resources/views/emails/registered-html.twig +++ b/resources/views/emails/registered-html.twig @@ -62,6 +62,7 @@ + diff --git a/resources/views/rules/partials/trigger.twig b/resources/views/rules/partials/trigger.twig index 60cd67c9e2..bc5df80649 100644 --- a/resources/views/rules/partials/trigger.twig +++ b/resources/views/rules/partials/trigger.twig @@ -23,9 +23,9 @@ {% if errors.has(('rule-trigger-value.'~count)) %} -

    - {{ errors.first('rule-trigger-value.'~count) }} -

    +

    + {{ errors.first('rule-trigger-value.'~count) }} +

    {% endif %} diff --git a/resources/views/rules/rule/delete.twig b/resources/views/rules/rule/delete.twig index 45571583a4..c468dcfa47 100644 --- a/resources/views/rules/rule/delete.twig +++ b/resources/views/rules/rule/delete.twig @@ -1,4 +1,3 @@ - {% extends "./layout/default.twig" %} {% block breadcrumbs %} From 1b316e462e6a86c1b795dfb04259ea69a58d5e27 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Jan 2016 06:34:50 +0100 Subject: [PATCH 184/276] Also fire rules when updating. --- app/Handlers/Events/FireRulesForStore.php | 1 - app/Handlers/Events/FireRulesForUpdate.php | 33 +++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/Handlers/Events/FireRulesForStore.php b/app/Handlers/Events/FireRulesForStore.php index 96853ad607..2adb382390 100644 --- a/app/Handlers/Events/FireRulesForStore.php +++ b/app/Handlers/Events/FireRulesForStore.php @@ -45,7 +45,6 @@ class FireRulesForStore */ public function handle(TransactionJournalStored $event) { - Log::debug('Before event (in handle). From account name is: ' . $event->journal->source_account->name); // get all the user's rule groups, with the rules, order by 'order'. /** @var User $user */ $user = Auth::user(); diff --git a/app/Handlers/Events/FireRulesForUpdate.php b/app/Handlers/Events/FireRulesForUpdate.php index a88a097e85..6e891626fc 100644 --- a/app/Handlers/Events/FireRulesForUpdate.php +++ b/app/Handlers/Events/FireRulesForUpdate.php @@ -9,7 +9,12 @@ namespace FireflyIII\Handlers\Events; +use Auth; use FireflyIII\Events\TransactionJournalUpdated; +use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Rules\Processor; +use FireflyIII\User; use Log; /** @@ -37,7 +42,33 @@ class FireRulesForUpdate */ public function handle(TransactionJournalUpdated $event) { - Log::debug('Fire rules for update!'); + // get all the user's rule groups, with the rules, order by 'order'. + /** @var User $user */ + $user = Auth::user(); + $groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); + // + /** @var RuleGroup $group */ + foreach ($groups as $group) { + Log::debug('Now processing group "' . $group->title . '".'); + $rules = $group->rules() + ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') + ->where('rule_triggers.trigger_type', 'user_action') + ->where('rule_triggers.trigger_value', 'update-journal') + ->where('rules.active', 1) + ->get(['rules.*']); + /** @var Rule $rule */ + foreach ($rules as $rule) { + Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')'); + $processor = new Processor($rule, $event->journal); + // get some return out of this? + $processor->handle(); + + if ($rule->stop_processing) { + break; + } + + } + } } } \ No newline at end of file From e834489206cb8fcd87b18b1f02ae205b8bca6719 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Jan 2016 07:14:36 +0100 Subject: [PATCH 185/276] Some cleaning up courtesy of PHPStorm. --- .../Budget/ChartJsBudgetChartGenerator.php | 3 +- .../ChartJsCategoryChartGenerator.php | 3 +- app/Helpers/Report/ReportHelper.php | 6 --- app/Http/Controllers/ProfileController.php | 1 - app/Http/Controllers/RuleController.php | 1 - .../Controllers/TransactionController.php | 2 - app/Repositories/Bill/BillRepository.php | 3 +- .../2014_12_13_190730_changes_for_v321.php | 3 +- .../2015_04_26_054507_changes_for_v3310.php | 2 +- ...2015_05_28_041652_entrust_setup_tables.php | 3 ++ .../2015_06_14_093841_changes_for_v345.php | 3 ++ .../2015_07_03_102450_changes_for_v3462.php | 2 + .../2015_07_14_204645_changes_for_v349.php | 2 + .../2015_07_17_190438_changes_for_v3410.php | 2 + .../2016_01_11_193428_changes_for_v370.php | 3 ++ database/seeds/TestDataSeeder.php | 39 +++++++++++-------- tests/BasicTest.php | 7 ++-- tests/TestCase.php | 3 ++ 18 files changed, 51 insertions(+), 37 deletions(-) diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index 77678b93e9..fa25cd857b 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -156,7 +156,8 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator ]; // get labels from one of the budgets (assuming there's at least one): $first = $entries->first(); - foreach ($first['budgeted'] as $year => $noInterest) { + $keys = array_keys($first['budgeted']); + foreach ($keys as $year) { $data['labels'][] = strval($year); } diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index 07debb7581..3c744cb9de 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -124,7 +124,8 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator ]; // get labels from one of the categories (assuming there's at least one): $first = $entries->first(); - foreach ($first['spent'] as $year => $noInterest) { + $keys = array_keys($first['spent']); + foreach ($keys as $year) { $data['labels'][] = strval($year); } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 7daede4b45..2b13fbe9ae 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -342,12 +342,6 @@ class ReportHelper implements ReportHelperInterface */ public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) { - // /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ - // $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); - - // /** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */ - // $tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface'); - $balance = new Balance; // build a balance header: diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index da581288e5..f8253d7408 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -1,7 +1,6 @@ getJournalData(); // if not withdrawal, unset budgetid. @@ -303,7 +302,6 @@ class TransactionController extends Controller $journal = $repository->store($journalData); - // save attachments: $att->saveAttachmentsForModel($journal); // flash errors diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 29bd373877..daa5a2a698 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -540,8 +540,7 @@ class BillRepository implements BillRepositoryInterface /** @var Account $creditCard */ foreach ($creditCards as $creditCard) { if ($creditCard->balance == 0) { - // find a transfer TO the credit card which should account for - // anything paid. If not, the CC is not yet used. + // find a transfer TO the credit card which should account for anything paid. If not, the CC is not yet used. $set = TransactionJournal::whereIn( 'transaction_journals.id', function (Builder $q) use ($creditCard, $start, $end) { $q->select('transaction_journals.id') diff --git a/database/migrations/2014_12_13_190730_changes_for_v321.php b/database/migrations/2014_12_13_190730_changes_for_v321.php index a893f8c0a7..a10d7785ae 100644 --- a/database/migrations/2014_12_13_190730_changes_for_v321.php +++ b/database/migrations/2014_12_13_190730_changes_for_v321.php @@ -10,7 +10,8 @@ use Illuminate\Support\Facades\Schema; /** * @SuppressWarnings(PHPMD.ShortMethodName) // method names are mandated by laravel. - * @SuppressWarnings("TooManyMethods") // I'm fine with this + * @SuppressWarnings(PHPMD.TooManyMethods) + * * * Down: * 1. Create new Components based on Budgets. diff --git a/database/migrations/2015_04_26_054507_changes_for_v3310.php b/database/migrations/2015_04_26_054507_changes_for_v3310.php index 3598736a21..7073e1b6b1 100644 --- a/database/migrations/2015_04_26_054507_changes_for_v3310.php +++ b/database/migrations/2015_04_26_054507_changes_for_v3310.php @@ -5,7 +5,7 @@ use Illuminate\Database\Schema\Blueprint; /** * @SuppressWarnings(PHPMD.ShortMethodName) - * @SuppressWarnings("PHPMD.ExcessiveMethodLength") + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * * Class ChangesForV3310 */ diff --git a/database/migrations/2015_05_28_041652_entrust_setup_tables.php b/database/migrations/2015_05_28_041652_entrust_setup_tables.php index 270f46a768..f18279e3a5 100644 --- a/database/migrations/2015_05_28_041652_entrust_setup_tables.php +++ b/database/migrations/2015_05_28_041652_entrust_setup_tables.php @@ -4,6 +4,9 @@ use Illuminate\Database\Schema\Blueprint; /** * Class EntrustSetupTables + * + * @SuppressWarnings(PHPMD.ShortMethodName) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ class EntrustSetupTables extends Migration { diff --git a/database/migrations/2015_06_14_093841_changes_for_v345.php b/database/migrations/2015_06_14_093841_changes_for_v345.php index 43a1df7223..752a0a54f9 100644 --- a/database/migrations/2015_06_14_093841_changes_for_v345.php +++ b/database/migrations/2015_06_14_093841_changes_for_v345.php @@ -5,6 +5,9 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV345 + * + * @SuppressWarnings(PHPMD.ShortMethodName) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ class ChangesForV345 extends Migration { diff --git a/database/migrations/2015_07_03_102450_changes_for_v3462.php b/database/migrations/2015_07_03_102450_changes_for_v3462.php index 930c8b0bab..d18d7c232c 100644 --- a/database/migrations/2015_07_03_102450_changes_for_v3462.php +++ b/database/migrations/2015_07_03_102450_changes_for_v3462.php @@ -5,6 +5,8 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV3462 + * + * @SuppressWarnings(PHPMD.ShortMethodName) */ class ChangesForV3462 extends Migration { diff --git a/database/migrations/2015_07_14_204645_changes_for_v349.php b/database/migrations/2015_07_14_204645_changes_for_v349.php index 3f2538c339..48147e6448 100644 --- a/database/migrations/2015_07_14_204645_changes_for_v349.php +++ b/database/migrations/2015_07_14_204645_changes_for_v349.php @@ -5,6 +5,8 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV349 + * + * @SuppressWarnings(PHPMD.ShortMethodName) */ class ChangesForV349 extends Migration { diff --git a/database/migrations/2015_07_17_190438_changes_for_v3410.php b/database/migrations/2015_07_17_190438_changes_for_v3410.php index c8e64213d9..feebb8396b 100644 --- a/database/migrations/2015_07_17_190438_changes_for_v3410.php +++ b/database/migrations/2015_07_17_190438_changes_for_v3410.php @@ -5,6 +5,8 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV3410 + * + * @SuppressWarnings(PHPMD.ShortMethodName) */ class ChangesForV3410 extends Migration { diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php index 6eed8b823b..87134f0581 100644 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ b/database/migrations/2016_01_11_193428_changes_for_v370.php @@ -12,6 +12,9 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV370 + * + * @SuppressWarnings(PHPMD.ShortMethodName) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ class ChangesForV370 extends Migration { diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 49550f1b0a..8aec28e0b8 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -23,6 +23,8 @@ use Illuminate\Database\Seeder; /** * * Class TestDataSeeder + * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.TooManyMethods) */ class TestDataSeeder extends Seeder { @@ -39,6 +41,9 @@ class TestDataSeeder extends Seeder } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function createRules() { // three rule groups to get started. @@ -433,26 +438,10 @@ class TestDataSeeder extends Seeder $ruleAction->action_value = 'Bills'; $ruleAction->save(); unset($ruleAction); - - - // a normal rule: saves transactions where description contains "groceries" - // and from account is "MyBank Checking Account" - // send it to Groceries/Groceries - - - //$ruleAction - - // TODO a rule that triggers on something, just like the previous one, but it has "stop_processing" set to 1. - // TODO a rule that triggers on that same thing, but it will not file, because the previous rule made FF skip it. - - // TODO rule with specific actions. - // TODO rule with actions and one action has stop_processing and other actions are not processed. Somewhere in test? - - } /** - * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function run() { @@ -545,6 +534,9 @@ class TestDataSeeder extends Seeder } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ protected function createAssetAccounts() { $assets = ['MyBank Checking Account', 'Savings', 'Shared', 'Creditcard', 'Emergencies', 'STE']; @@ -663,6 +655,9 @@ class TestDataSeeder extends Seeder ); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ protected function createPiggybanks() { $account = $this->findAccount('Savings'); @@ -820,6 +815,8 @@ class TestDataSeeder extends Seeder * @param Carbon $date * @param $amount * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * * @return TransactionJournal */ protected function createIncome($description, Carbon $date, $amount) @@ -1056,6 +1053,7 @@ class TestDataSeeder extends Seeder /** * @param Carbon $date + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function createGroceries(Carbon $date) { @@ -1113,6 +1111,7 @@ class TestDataSeeder extends Seeder /** * @param Carbon $date + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function createDrinksAndOthers(Carbon $date) { @@ -1331,6 +1330,12 @@ class TestDataSeeder extends Seeder return null; } + /** + * @param $date + * + * @return static + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ protected function createCar($date) { // twice: diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 0f1b9f46d4..079f2790df 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -1,9 +1,8 @@ Date: Sat, 16 Jan 2016 09:15:24 +0100 Subject: [PATCH 186/276] Some new code for testing. --- .gitignore | 1 + config/database.php | 2 +- database/seeds/DatabaseSeeder.php | 8 +- database/seeds/TestDataSeeder.php | 1411 +--------------------- database/seeds/VisualTestDataSeeder.php | 1424 +++++++++++++++++++++++ pu.sh | 41 + tests/TestCase.php | 54 +- 7 files changed, 1530 insertions(+), 1411 deletions(-) create mode 100644 database/seeds/VisualTestDataSeeder.php create mode 100755 pu.sh diff --git a/.gitignore b/.gitignore index b4883da65a..b59bed9bb4 100755 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ storage/ .project .settings/ +.env.local diff --git a/config/database.php b/config/database.php index 5865a2f758..9467c969e6 100755 --- a/config/database.php +++ b/config/database.php @@ -48,7 +48,7 @@ return [ 'sqlite' => [ 'driver' => 'sqlite', - 'database' => storage_path('database.sqlite'), + 'database' => storage_path('database') . '/testing.db', 'prefix' => '', ], diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 9adb4e5c34..fa57be20b1 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -22,9 +22,15 @@ class DatabaseSeeder extends Seeder $this->call('TransactionTypeSeeder'); $this->call('PermissionSeeder'); - if (App::environment() == 'testing' || App::environment() == 'homestead' || gethostname() == 'lightning') { + // set up basic test data (as little as possible): + if (App::environment() == 'testing') { $this->call('TestDataSeeder'); } + + // this one is reserved for more extensive testing. + //if (App::environment() == 'testing' || App::environment() == 'homestead' || gethostname() == 'lightning') { + //$this->call('VisualTestDataSeeder'); + //} } } diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 8aec28e0b8..a76de1b7e0 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -1,1424 +1,19 @@ user()->associate($this->user); - $ruleGroup->order = 1; - $ruleGroup->active = 1; - $ruleGroup->title = 'Default rules'; - $ruleGroup->description = 'All your rules not in a particular group.'; - $ruleGroup->save(); - unset($ruleGroup); - - $ruleGroup = new RuleGroup; - $ruleGroup->user()->associate($this->user); - $ruleGroup->order = 2; - $ruleGroup->active = 1; - $ruleGroup->title = 'Empty rule group'; - $ruleGroup->description = 'Intentionally has no rules.'; - $ruleGroup->save(); - unset($ruleGroup); - - - $ruleGroup = new RuleGroup; - $ruleGroup->user()->associate($this->user); - $ruleGroup->order = 3; - $ruleGroup->active = 1; - $ruleGroup->title = 'Rules for bills'; - $ruleGroup->description = 'All rules for bills and recurring costs.'; - $ruleGroup->save(); - unset($ruleGroup); - - // move groceries to correct budget/category - $rule = new Rule; - $rule->user()->associate($this->user); - $rule->ruleGroup()->associate(RuleGroup::find(1)); - $rule->order = 1; - $rule->active = 1; - $rule->stop_processing = 0; - $rule->title = 'Move groceries'; - $rule->description = 'Move groceries to correct category and budget.'; - - $rule->save(); - - // initial trigger for this rule: - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 1; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'user_action'; - $ruleTrigger->trigger_value = 'store-journal'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // content trigger for this rule. - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 2; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'description_contains'; - $ruleTrigger->trigger_value = 'groceries'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // another - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 3; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'from_account_is'; - $ruleTrigger->trigger_value = 'MyBank Checking Account'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // actions for this rule. one, set category - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 1; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_category'; - $ruleAction->action_value = 'Groceries'; - $ruleAction->save(); - unset($ruleAction); - - // actions for this rule. one, set budget - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 2; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_budget'; - $ruleAction->action_value = 'Groceries'; - $ruleAction->save(); - unset($ruleAction); - - - // move "gas" to "Car" and "Car" - $rule = new Rule; - $rule->user()->associate($this->user); - $rule->ruleGroup()->associate(RuleGroup::find(1)); - $rule->order = 2; - $rule->active = 1; - $rule->stop_processing = 0; - $rule->title = 'Move gas'; - $rule->description = null; - - $rule->save(); - - // initial trigger for this rule: - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 1; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'user_action'; - $ruleTrigger->trigger_value = 'store-journal'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // content trigger for this rule. - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 2; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'description_contains'; - $ruleTrigger->trigger_value = 'gas'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // another - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 3; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'from_account_is'; - $ruleTrigger->trigger_value = 'MyBank Checking Account'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // actions for this rule. one, set category - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 1; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_category'; - $ruleAction->action_value = 'Car'; - $ruleAction->save(); - unset($ruleAction); - - // actions for this rule. one, set budget - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 2; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_budget'; - $ruleAction->action_value = 'Car'; - $ruleAction->save(); - unset($ruleAction); - - // move savings to money management - $rule = new Rule; - $rule->user()->associate($this->user); - $rule->ruleGroup()->associate(RuleGroup::find(1)); - $rule->order = 3; - $rule->active = 1; - $rule->stop_processing = 0; - $rule->title = 'Move savings'; - $rule->description = null; - - $rule->save(); - - // initial trigger for this rule: - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 1; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'user_action'; - $ruleTrigger->trigger_value = 'store-journal'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // is transfer - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 2; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'transaction_type'; - $ruleTrigger->trigger_value = 'Transfer'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // content trigger for this rule. - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 3; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'description_is'; - $ruleTrigger->trigger_value = 'Save money'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // another - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 4; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'from_account_is'; - $ruleTrigger->trigger_value = 'MyBank Checking Account'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // another - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 5; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'to_account_is'; - $ruleTrigger->trigger_value = 'Savings'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // actions for this rule. one, set category - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 1; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_category'; - $ruleAction->action_value = 'Money Management'; - $ruleAction->save(); - unset($ruleAction); - - // move TV bill to "Bills" and "House" - $rule = new Rule; - $rule->user()->associate($this->user); - $rule->ruleGroup()->associate(RuleGroup::find(3)); - $rule->order = 1; - $rule->active = 1; - $rule->stop_processing = 0; - $rule->title = 'TV Bill'; - $rule->description = null; - - $rule->save(); - - // initial trigger for this rule: - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 1; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'user_action'; - $ruleTrigger->trigger_value = 'store-journal'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // content trigger for this rule. - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 2; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'description_contains'; - $ruleTrigger->trigger_value = 'tv bill'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // another - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 3; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'from_account_is'; - $ruleTrigger->trigger_value = 'MyBank Checking Account'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // actions for this rule. one, set category - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 1; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_category'; - $ruleAction->action_value = 'House'; - $ruleAction->save(); - unset($ruleAction); - - // actions for this rule. one, set budget - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 2; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_budget'; - $ruleAction->action_value = 'Bills'; - $ruleAction->save(); - unset($ruleAction); - - // move rent to bills, rent. - $rule = new Rule; - $rule->user()->associate($this->user); - $rule->ruleGroup()->associate(RuleGroup::find(3)); - $rule->order = 2; - $rule->active = 1; - $rule->stop_processing = 1; - $rule->title = 'Rent'; - $rule->description = 'Do something with rent.'; - - $rule->save(); - - // initial trigger for this rule: - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 1; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'user_action'; - $ruleTrigger->trigger_value = 'update-journal'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // content trigger for this rule. - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 2; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 0; - $ruleTrigger->trigger_type = 'description_contains'; - $ruleTrigger->trigger_value = 'rent'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // another - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = 3; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = 1; - $ruleTrigger->trigger_type = 'from_account_is'; - $ruleTrigger->trigger_value = 'MyBank Checking Account'; - - $ruleTrigger->save(); - unset($ruleTrigger); - - // actions for this rule. one, set category - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 1; - $ruleAction->active = 1; - $ruleAction->stop_processing = 0; - $ruleAction->action_type = 'set_category'; - $ruleAction->action_value = 'House'; - $ruleAction->save(); - unset($ruleAction); - - // actions for this rule. one, set budget - $ruleAction = new RuleAction; - $ruleAction->rule()->associate($rule); - $ruleAction->order = 2; - $ruleAction->active = 1; - $ruleAction->stop_processing = 1; - $ruleAction->action_type = 'set_budget'; - $ruleAction->action_value = 'Bills'; - $ruleAction->save(); - unset($ruleAction); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return void */ public function run() { - $this->createUsers(); - - // create accounts: - $this->createAssetAccounts(); - $this->createExpenseAccounts(); - $this->createRevenueAccounts(); - $this->createBills(); - $this->createPiggybanks(); - - $this->createRules(); - - // preference to only see account #1 on frontpage. - $this->createPreferences(); - - // dates: - $start = Carbon::now()->subYears(2)->startOfMonth(); - $end = Carbon::now()->endOfDay(); - - - $current = clone $start; - while ($current < $end) { - $month = $current->format('F Y'); - // create salaries: - $this->createIncome('Salary ' . $month, $current, rand(2000, 2100)); - - // pay bills: - $this->createRent('Rent for ' . $month, $current, 800); - $this->createWater('Water bill for ' . $month, $current, 15); - $this->createTV('TV bill for ' . $month, $current, 60); - $this->createPower('Power bill for ' . $month, $current, 120); - - - // pay daily groceries: - $this->createGroceries($current); - - // create tag (each type of tag, for date): - $this->createTags($current); - - // go out for drinks: - $this->createDrinksAndOthers($current); - - // save money every month: - $this->createSavings($current); - - // buy gas for the car every month: - $this->createCar($current); - - // budget limit for this month, on "Groceries". - $this->createBudgetLimit($current, 'Groceries', 400); - $this->createBudgetLimit($current, 'Bills', 1000); - $this->createBudgetLimit($current, 'Car', 100); - - echo 'Created test data for ' . $month . "\n"; - $current->addMonth(); - } } - - /** - * @param Carbon $date - */ - protected function createTags(Carbon $date) - { - Tag::create( - [ - 'user_id' => $this->user->id, - 'tag' => 'SomeTag' . $date->month . '.' . $date->year . '.nothing', - 'tagMode' => 'nothing', - 'date' => $date->format('Y-m-d'), - - - ] - ); - } - - /** - * - */ - protected function createUsers() - { - User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]); - $this->user = User::whereEmail('thegrumpydictator@gmail.com')->first(); - - // create rights: - $role = Role::find(1); - $this->user->roles()->save($role); - - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function createAssetAccounts() - { - $assets = ['MyBank Checking Account', 'Savings', 'Shared', 'Creditcard', 'Emergencies', 'STE']; - $ibans = ['NL47JDYU6179706202', 'NL51WGBP5832453599', 'NL81RCQZ7160379858', 'NL19NRAP2367994221', 'NL40UKBK3619908726', 'NL38SRMN4325934708']; - $assetMeta = [ - [ - 'accountRole' => 'defaultAsset', - ], - [ - 'accountRole' => 'savingAsset', - ], - [ - 'accountRole' => 'sharedAsset', - ], - [ - 'accountRole' => 'ccAsset', - 'ccMonthlyPaymentDate' => '2015-05-27', - 'ccType' => 'monthlyFull', - ], - [ - 'accountRole' => 'savingAsset', - ], - [ - 'accountRole' => 'savingAsset', - ], - - ]; - - foreach ($assets as $index => $name) { - // create account: - $account = Account::create( - [ - 'user_id' => $this->user->id, - 'account_type_id' => 3, - 'name' => $name, - 'active' => 1, - 'encrypted' => 1, - 'iban' => $ibans[$index], - ] - ); - foreach ($assetMeta[$index] as $name => $value) { - AccountMeta::create(['account_id' => $account->id, 'name' => $name, 'data' => $value,]); - } - } - } - - protected function createExpenseAccounts() - { - $expenses = ['Adobe', 'Google', 'Vitens', 'Albert Heijn', 'PLUS', 'Apple', 'Bakker', 'Belastingdienst', 'bol.com', 'Cafe Central', 'conrad.nl', - 'coolblue', 'Shell', - 'DUO', 'Etos', 'FEBO', 'Greenchoice', 'Halfords', 'XS4All', 'iCentre', 'Jumper', 'Land lord']; - foreach ($expenses as $name) { - // create account: - Account::create( - [ - 'user_id' => $this->user->id, - 'account_type_id' => 4, - 'name' => $name, - 'active' => 1, - 'encrypted' => 1, - ] - ); - } - - } - - /** - * - */ - protected function createRevenueAccounts() - { - $revenues = ['Job', 'Belastingdienst', 'Bank', 'KPN', 'Google']; - foreach ($revenues as $name) { - // create account: - Account::create( - [ - 'user_id' => $this->user->id, - 'account_type_id' => 5, - 'name' => $name, - 'active' => 1, - 'encrypted' => 1, - ] - ); - } - } - - public function createBills() - { - Bill::create( - [ - 'name' => 'Rent', - 'match' => 'rent,land,lord', - 'amount_min' => 795, - 'amount_max' => 805, - 'user_id' => $this->user->id, - 'date' => '2015-01-01', - 'active' => 1, - 'automatch' => 1, - 'repeat_freq' => 'monthly', - 'skip' => 0, - ] - ); - Bill::create( - [ - 'name' => 'Health insurance', - 'match' => 'zilveren,kruis,health', - 'amount_min' => 120, - 'amount_max' => 140, - 'user_id' => $this->user->id, - 'date' => '2015-01-01', - 'active' => 1, - 'automatch' => 1, - 'repeat_freq' => 'monthly', - 'skip' => 0, - ] - ); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function createPiggybanks() - { - $account = $this->findAccount('Savings'); - - $camera = PiggyBank::create( - [ - 'account_id' => $account->id, - 'name' => 'New camera', - 'targetamount' => 1000, - 'startdate' => '2015-04-01', - 'reminder_skip' => 0, - 'remind_me' => 0, - 'order' => 1, - ] - ); - $repetition = $camera->piggyBankRepetitions()->first(); - $repetition->currentamount = 735; - $repetition->save(); - - // events: - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $camera->id, - 'date' => '2015-05-01', - 'amount' => '245', - ] - ); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $camera->id, - 'date' => '2015-06-01', - 'amount' => '245', - ] - ); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $camera->id, - 'date' => '2015-07-01', - 'amount' => '245', - ] - ); - - - $phone = PiggyBank::create( - [ - 'account_id' => $account->id, - 'name' => 'New phone', - 'targetamount' => 600, - 'startdate' => '2015-04-01', - 'reminder_skip' => 0, - 'remind_me' => 0, - 'order' => 2, - ] - ); - $repetition = $phone->piggyBankRepetitions()->first(); - $repetition->currentamount = 333; - $repetition->save(); - - // events: - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $phone->id, - 'date' => '2015-05-01', - 'amount' => '111', - ] - ); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $phone->id, - 'date' => '2015-06-01', - 'amount' => '111', - ] - ); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $phone->id, - 'date' => '2015-07-01', - 'amount' => '111', - ] - ); - - $couch = PiggyBank::create( - [ - 'account_id' => $account->id, - 'name' => 'New couch', - 'targetamount' => 500, - 'startdate' => '2015-04-01', - 'reminder_skip' => 0, - 'remind_me' => 0, - 'order' => 3, - ] - ); - $repetition = $couch->piggyBankRepetitions()->first(); - $repetition->currentamount = 120; - $repetition->save(); - - // events: - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $couch->id, - 'date' => '2015-05-01', - 'amount' => '40', - ] - ); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $couch->id, - 'date' => '2015-06-01', - 'amount' => '40', - ] - ); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $couch->id, - 'date' => '2015-07-01', - 'amount' => '40', - ] - ); - - // empty one. - PiggyBank::create( - [ - 'account_id' => $account->id, - 'name' => 'New head set', - 'targetamount' => 500, - 'startdate' => '2015-04-01', - 'reminder_skip' => 0, - 'remind_me' => 0, - 'order' => 4, - ] - ); - - } - - /** - * @param $name - * - * @return Account|null - */ - protected function findAccount($name) - { - /** @var Account $account */ - foreach (Account::get() as $account) { - if ($account->name == $name && $this->user->id == $account->user_id) { - return $account; - break; - } - } - - return null; - } - - /** - * @param $description - * @param Carbon $date - * @param $amount - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * - * @return TransactionJournal - */ - protected function createIncome($description, Carbon $date, $amount) - { - $date = new Carbon($date->format('Y-m') . '-23'); // paid on 23rd. - $today = new Carbon; - if ($date >= $today) { - return null; - } - $toAccount = $this->findAccount('MyBank Checking Account'); - $fromAccount = $this->findAccount('Job'); - $category = Category::firstOrCreateEncrypted(['name' => 'Salary', 'user_id' => $this->user->id]); - // create journal: - - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 2, - 'transaction_currency_id' => 1, - 'description' => $description, - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - - return $journal; - - } - - /** - * @param $description - * @param Carbon $date - * @param $amount - * - * @return TransactionJournal - */ - protected function createRent($description, Carbon $date, $amount) - { - $fromAccount = $this->findAccount('MyBank Checking Account'); - $toAccount = $this->findAccount('Land lord'); - $category = Category::firstOrCreateEncrypted(['name' => 'Rent', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'bill_id' => 1, - 'description' => $description, - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - return $journal; - - } - - /** - * @param $description - * @param Carbon $date - * @param $amount - * - * @return TransactionJournal - */ - protected function createWater($description, Carbon $date, $amount) - { - $date = new Carbon($date->format('Y-m') . '-10'); // paid on 10th - $fromAccount = $this->findAccount('MyBank Checking Account'); - $toAccount = $this->findAccount('Vitens'); - $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => $description, - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - return $journal; - - } - - /** - * @param $description - * @param Carbon $date - * @param $amount - * - * @return TransactionJournal - */ - protected function createTV($description, Carbon $date, $amount) - { - $date = new Carbon($date->format('Y-m') . '-15'); // paid on 10th - $fromAccount = $this->findAccount('MyBank Checking Account'); - $toAccount = $this->findAccount('XS4All'); - $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => $description, - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - return $journal; - - } - - /** - * @param $description - * @param Carbon $date - * @param $amount - * - * @return TransactionJournal - */ - protected function createPower($description, Carbon $date, $amount) - { - $date = new Carbon($date->format('Y-m') . '-06'); // paid on 10th - $fromAccount = $this->findAccount('MyBank Checking Account'); - $toAccount = $this->findAccount('Greenchoice'); - $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => $description, - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - return $journal; - - } - - /** - * @param Carbon $date - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function createGroceries(Carbon $date) - { - $start = clone $date; - $end = clone $date; - $today = new Carbon; - $start->startOfMonth(); - $end->endOfMonth(); - - $fromAccount = $this->findAccount('MyBank Checking Account'); - $stores = ['Albert Heijn', 'PLUS', 'Bakker']; - $descriptions = ['Groceries', 'Bought some groceries', 'Got groceries']; - $category = Category::firstOrCreateEncrypted(['name' => 'Daily groceries', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]); - - $current = clone $start; - while ($current < $end && $current < $today) { - // daily groceries: - $amount = rand(1500, 2500) / 100; - $toAccount = $this->findAccount($stores[rand(0, count($stores) - 1)]); - - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => $descriptions[rand(0, count($descriptions) - 1)], - 'completed' => 1, - 'date' => $current, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - - $current->addDay(); - } - } - - /** - * @param Carbon $date - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function createDrinksAndOthers(Carbon $date) - { - $start = clone $date; - $end = clone $date; - $today = new Carbon; - $start->startOfMonth(); - $end->endOfMonth(); - $current = clone $start; - while ($current < $end && $current < $today) { - - // weekly drink: - $thisDate = clone $current; - $thisDate->addDay(); - $fromAccount = $this->findAccount('MyBank Checking Account'); - $toAccount = $this->findAccount('Cafe Central'); - $category = Category::firstOrCreateEncrypted(['name' => 'Drinks', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Going out', 'user_id' => $this->user->id]); - $amount = rand(1500, 3600) / 100; - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => 'Going out for drinks', - 'completed' => 1, - 'date' => $thisDate, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - // shopping at some (online) shop: - - - $current->addWeek(); - } - } - - /** - * @param Carbon $date - * - * @return TransactionJournal - */ - protected function createSavings(Carbon $date) - { - $date = new Carbon($date->format('Y-m') . '-24'); // paid on 24th. - $toAccount = $this->findAccount('Savings'); - $fromAccount = $this->findAccount('MyBank Checking Account'); - $category = Category::firstOrCreateEncrypted(['name' => 'Money management', 'user_id' => $this->user->id]); - // create journal: - - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 3, - 'transaction_currency_id' => 1, - 'description' => 'Save money', - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => -150, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => 150, - - ] - ); - $journal->categories()->save($category); - - return $journal; - - } - - /** - * @param Carbon $current - * @param $name - * @param $amount - */ - protected function createBudgetLimit(Carbon $current, $name, $amount) - { - $start = clone $current; - $end = clone $current; - $budget = $this->findBudget($name); - $start->startOfMonth(); - $end->endOfMonth(); - - BudgetLimit::create( - [ - 'budget_id' => $budget->id, - 'startdate' => $start->format('Y-m-d'), - 'amount' => $amount, - 'repeats' => 0, - 'repeat_freq' => 'monthly', - ] - ); - } - - /** - * @param $name - * - * @return Budget|null - */ - protected function findBudget($name) - { - /** @var Budget $budget */ - foreach (Budget::get() as $budget) { - if ($budget->name == $name && $this->user->id == $budget->user_id) { - return $budget; - break; - } - } - - return null; - } - - /** - * @param $name - * - * @return Bill|null - */ - protected function findBill($name) - { - /** @var Bill $bill */ - foreach (Bill::get() as $bill) { - if ($bill->name == $name && $this->user->id == $bill->user_id) { - return $bill; - break; - } - } - - return null; - } - - /** - * @param $name - * - * @return Category|null - */ - protected function findCategory($name) - { - - /** @var Category $category */ - foreach (Category::get() as $category) { - if ($category->name == $name && $this->user->id == $category->user_id) { - return $category; - break; - } - } - - return null; - } - - /** - * @param $name - * - * @return PiggyBank|null - */ - protected function findPiggyBank($name) - { - - /** @var Budget $budget */ - foreach (PiggyBank::get() as $piggyBank) { - $account = $piggyBank->account()->first(); - if ($piggyBank->name == $name && $this->user->id == $account->user_id) { - return $piggyBank; - break; - } - } - - return null; - } - - /** - * @param $tagName - * - * @return Tag|null - * @internal param $tag - */ - protected function findTag($tagName) - { - /** @var Tag $tag */ - foreach (Tag::get() as $tag) { - if ($tag->tag == $tagName && $this->user->id == $tag->user_id) { - return $tag; - break; - } - } - - return null; - } - - /** - * @param $date - * - * @return static - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function createCar($date) - { - // twice: - $date = new Carbon($date->format('Y-m') . '-10'); // paid on 10th - $fromAccount = $this->findAccount('MyBank Checking Account'); - $toAccount = $this->findAccount('Shell'); - $category = Category::firstOrCreateEncrypted(['name' => 'Car', 'user_id' => $this->user->id]); - $budget = Budget::firstOrCreateEncrypted(['name' => 'Car', 'user_id' => $this->user->id]); - $amount = rand(4000, 5000) / 100; - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => 'Bought gas', - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - $journal->categories()->save($category); - $journal->budgets()->save($budget); - - // and again! - $date = new Carbon($date->format('Y-m') . '-20'); // paid on 20th - $amount = rand(4000, 5000) / 100; - - - $journal = TransactionJournal::create( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => 1, - 'transaction_currency_id' => 1, - 'description' => 'Gas for car', - 'completed' => 1, - 'date' => $date, - ] - ); - Transaction::create( - [ - 'account_id' => $fromAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount * -1, - - ] - ); - Transaction::create( - [ - 'account_id' => $toAccount->id, - 'transaction_journal_id' => $journal->id, - 'amount' => $amount, - - ] - ); - - // and again! - - return $journal; - } - - protected function createPreferences() - { - $preference = new Preference; - $preference->name = 'frontPageAccounts'; - $preference->data = [1]; - $preference->user()->associate($this->user); - $preference->save(); - } - - } diff --git a/database/seeds/VisualTestDataSeeder.php b/database/seeds/VisualTestDataSeeder.php new file mode 100644 index 0000000000..b099d2db30 --- /dev/null +++ b/database/seeds/VisualTestDataSeeder.php @@ -0,0 +1,1424 @@ +user()->associate($this->user); + $ruleGroup->order = 1; + $ruleGroup->active = 1; + $ruleGroup->title = 'Default rules'; + $ruleGroup->description = 'All your rules not in a particular group.'; + $ruleGroup->save(); + unset($ruleGroup); + + $ruleGroup = new RuleGroup; + $ruleGroup->user()->associate($this->user); + $ruleGroup->order = 2; + $ruleGroup->active = 1; + $ruleGroup->title = 'Empty rule group'; + $ruleGroup->description = 'Intentionally has no rules.'; + $ruleGroup->save(); + unset($ruleGroup); + + + $ruleGroup = new RuleGroup; + $ruleGroup->user()->associate($this->user); + $ruleGroup->order = 3; + $ruleGroup->active = 1; + $ruleGroup->title = 'Rules for bills'; + $ruleGroup->description = 'All rules for bills and recurring costs.'; + $ruleGroup->save(); + unset($ruleGroup); + + // move groceries to correct budget/category + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(1)); + $rule->order = 1; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'Move groceries'; + $rule->description = 'Move groceries to correct category and budget.'; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'groceries'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'Groceries'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Groceries'; + $ruleAction->save(); + unset($ruleAction); + + + // move "gas" to "Car" and "Car" + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(1)); + $rule->order = 2; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'Move gas'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'gas'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'Car'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Car'; + $ruleAction->save(); + unset($ruleAction); + + // move savings to money management + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(1)); + $rule->order = 3; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'Move savings'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // is transfer + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'transaction_type'; + $ruleTrigger->trigger_value = 'Transfer'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_is'; + $ruleTrigger->trigger_value = 'Save money'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 4; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 5; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'to_account_is'; + $ruleTrigger->trigger_value = 'Savings'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'Money Management'; + $ruleAction->save(); + unset($ruleAction); + + // move TV bill to "Bills" and "House" + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(3)); + $rule->order = 1; + $rule->active = 1; + $rule->stop_processing = 0; + $rule->title = 'TV Bill'; + $rule->description = null; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'store-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'tv bill'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'House'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Bills'; + $ruleAction->save(); + unset($ruleAction); + + // move rent to bills, rent. + $rule = new Rule; + $rule->user()->associate($this->user); + $rule->ruleGroup()->associate(RuleGroup::find(3)); + $rule->order = 2; + $rule->active = 1; + $rule->stop_processing = 1; + $rule->title = 'Rent'; + $rule->description = 'Do something with rent.'; + + $rule->save(); + + // initial trigger for this rule: + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 1; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'user_action'; + $ruleTrigger->trigger_value = 'update-journal'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // content trigger for this rule. + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 2; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 0; + $ruleTrigger->trigger_type = 'description_contains'; + $ruleTrigger->trigger_value = 'rent'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // another + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = 3; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = 1; + $ruleTrigger->trigger_type = 'from_account_is'; + $ruleTrigger->trigger_value = 'MyBank Checking Account'; + + $ruleTrigger->save(); + unset($ruleTrigger); + + // actions for this rule. one, set category + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 1; + $ruleAction->active = 1; + $ruleAction->stop_processing = 0; + $ruleAction->action_type = 'set_category'; + $ruleAction->action_value = 'House'; + $ruleAction->save(); + unset($ruleAction); + + // actions for this rule. one, set budget + $ruleAction = new RuleAction; + $ruleAction->rule()->associate($rule); + $ruleAction->order = 2; + $ruleAction->active = 1; + $ruleAction->stop_processing = 1; + $ruleAction->action_type = 'set_budget'; + $ruleAction->action_value = 'Bills'; + $ruleAction->save(); + unset($ruleAction); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function run() + { + $this->createUsers(); + + // create accounts: + $this->createAssetAccounts(); + $this->createExpenseAccounts(); + $this->createRevenueAccounts(); + $this->createBills(); + $this->createPiggybanks(); + + $this->createRules(); + + // preference to only see account #1 on frontpage. + $this->createPreferences(); + + // dates: + $start = Carbon::now()->subYears(2)->startOfMonth(); + $end = Carbon::now()->endOfDay(); + + + $current = clone $start; + while ($current < $end) { + $month = $current->format('F Y'); + // create salaries: + $this->createIncome('Salary ' . $month, $current, rand(2000, 2100)); + + // pay bills: + $this->createRent('Rent for ' . $month, $current, 800); + $this->createWater('Water bill for ' . $month, $current, 15); + $this->createTV('TV bill for ' . $month, $current, 60); + $this->createPower('Power bill for ' . $month, $current, 120); + + + // pay daily groceries: + $this->createGroceries($current); + + // create tag (each type of tag, for date): + $this->createTags($current); + + // go out for drinks: + $this->createDrinksAndOthers($current); + + // save money every month: + $this->createSavings($current); + + // buy gas for the car every month: + $this->createCar($current); + + // budget limit for this month, on "Groceries". + $this->createBudgetLimit($current, 'Groceries', 400); + $this->createBudgetLimit($current, 'Bills', 1000); + $this->createBudgetLimit($current, 'Car', 100); + + echo 'Created test data for ' . $month . "\n"; + $current->addMonth(); + } + + } + + /** + * @param Carbon $date + */ + protected function createTags(Carbon $date) + { + Tag::create( + [ + 'user_id' => $this->user->id, + 'tag' => 'SomeTag' . $date->month . '.' . $date->year . '.nothing', + 'tagMode' => 'nothing', + 'date' => $date->format('Y-m-d'), + + + ] + ); + } + + /** + * + */ + protected function createUsers() + { + User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]); + $this->user = User::whereEmail('thegrumpydictator@gmail.com')->first(); + + // create rights: + $role = Role::find(1); + $this->user->roles()->save($role); + + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function createAssetAccounts() + { + $assets = ['MyBank Checking Account', 'Savings', 'Shared', 'Creditcard', 'Emergencies', 'STE']; + $ibans = ['NL47JDYU6179706202', 'NL51WGBP5832453599', 'NL81RCQZ7160379858', 'NL19NRAP2367994221', 'NL40UKBK3619908726', 'NL38SRMN4325934708']; + $assetMeta = [ + [ + 'accountRole' => 'defaultAsset', + ], + [ + 'accountRole' => 'savingAsset', + ], + [ + 'accountRole' => 'sharedAsset', + ], + [ + 'accountRole' => 'ccAsset', + 'ccMonthlyPaymentDate' => '2015-05-27', + 'ccType' => 'monthlyFull', + ], + [ + 'accountRole' => 'savingAsset', + ], + [ + 'accountRole' => 'savingAsset', + ], + + ]; + + foreach ($assets as $index => $name) { + // create account: + $account = Account::create( + [ + 'user_id' => $this->user->id, + 'account_type_id' => 3, + 'name' => $name, + 'active' => 1, + 'encrypted' => 1, + 'iban' => $ibans[$index], + ] + ); + foreach ($assetMeta[$index] as $name => $value) { + AccountMeta::create(['account_id' => $account->id, 'name' => $name, 'data' => $value,]); + } + } + } + + protected function createExpenseAccounts() + { + $expenses = ['Adobe', 'Google', 'Vitens', 'Albert Heijn', 'PLUS', 'Apple', 'Bakker', 'Belastingdienst', 'bol.com', 'Cafe Central', 'conrad.nl', + 'coolblue', 'Shell', + 'DUO', 'Etos', 'FEBO', 'Greenchoice', 'Halfords', 'XS4All', 'iCentre', 'Jumper', 'Land lord']; + foreach ($expenses as $name) { + // create account: + Account::create( + [ + 'user_id' => $this->user->id, + 'account_type_id' => 4, + 'name' => $name, + 'active' => 1, + 'encrypted' => 1, + ] + ); + } + + } + + /** + * + */ + protected function createRevenueAccounts() + { + $revenues = ['Job', 'Belastingdienst', 'Bank', 'KPN', 'Google']; + foreach ($revenues as $name) { + // create account: + Account::create( + [ + 'user_id' => $this->user->id, + 'account_type_id' => 5, + 'name' => $name, + 'active' => 1, + 'encrypted' => 1, + ] + ); + } + } + + public function createBills() + { + Bill::create( + [ + 'name' => 'Rent', + 'match' => 'rent,land,lord', + 'amount_min' => 795, + 'amount_max' => 805, + 'user_id' => $this->user->id, + 'date' => '2015-01-01', + 'active' => 1, + 'automatch' => 1, + 'repeat_freq' => 'monthly', + 'skip' => 0, + ] + ); + Bill::create( + [ + 'name' => 'Health insurance', + 'match' => 'zilveren,kruis,health', + 'amount_min' => 120, + 'amount_max' => 140, + 'user_id' => $this->user->id, + 'date' => '2015-01-01', + 'active' => 1, + 'automatch' => 1, + 'repeat_freq' => 'monthly', + 'skip' => 0, + ] + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function createPiggybanks() + { + $account = $this->findAccount('Savings'); + + $camera = PiggyBank::create( + [ + 'account_id' => $account->id, + 'name' => 'New camera', + 'targetamount' => 1000, + 'startdate' => '2015-04-01', + 'reminder_skip' => 0, + 'remind_me' => 0, + 'order' => 1, + ] + ); + $repetition = $camera->piggyBankRepetitions()->first(); + $repetition->currentamount = 735; + $repetition->save(); + + // events: + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $camera->id, + 'date' => '2015-05-01', + 'amount' => '245', + ] + ); + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $camera->id, + 'date' => '2015-06-01', + 'amount' => '245', + ] + ); + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $camera->id, + 'date' => '2015-07-01', + 'amount' => '245', + ] + ); + + + $phone = PiggyBank::create( + [ + 'account_id' => $account->id, + 'name' => 'New phone', + 'targetamount' => 600, + 'startdate' => '2015-04-01', + 'reminder_skip' => 0, + 'remind_me' => 0, + 'order' => 2, + ] + ); + $repetition = $phone->piggyBankRepetitions()->first(); + $repetition->currentamount = 333; + $repetition->save(); + + // events: + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $phone->id, + 'date' => '2015-05-01', + 'amount' => '111', + ] + ); + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $phone->id, + 'date' => '2015-06-01', + 'amount' => '111', + ] + ); + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $phone->id, + 'date' => '2015-07-01', + 'amount' => '111', + ] + ); + + $couch = PiggyBank::create( + [ + 'account_id' => $account->id, + 'name' => 'New couch', + 'targetamount' => 500, + 'startdate' => '2015-04-01', + 'reminder_skip' => 0, + 'remind_me' => 0, + 'order' => 3, + ] + ); + $repetition = $couch->piggyBankRepetitions()->first(); + $repetition->currentamount = 120; + $repetition->save(); + + // events: + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $couch->id, + 'date' => '2015-05-01', + 'amount' => '40', + ] + ); + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $couch->id, + 'date' => '2015-06-01', + 'amount' => '40', + ] + ); + PiggyBankEvent::create( + [ + 'piggy_bank_id' => $couch->id, + 'date' => '2015-07-01', + 'amount' => '40', + ] + ); + + // empty one. + PiggyBank::create( + [ + 'account_id' => $account->id, + 'name' => 'New head set', + 'targetamount' => 500, + 'startdate' => '2015-04-01', + 'reminder_skip' => 0, + 'remind_me' => 0, + 'order' => 4, + ] + ); + + } + + /** + * @param $name + * + * @return Account|null + */ + protected function findAccount($name) + { + /** @var Account $account */ + foreach (Account::get() as $account) { + if ($account->name == $name && $this->user->id == $account->user_id) { + return $account; + break; + } + } + + return null; + } + + /** + * @param $description + * @param Carbon $date + * @param $amount + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return TransactionJournal + */ + protected function createIncome($description, Carbon $date, $amount) + { + $date = new Carbon($date->format('Y-m') . '-23'); // paid on 23rd. + $today = new Carbon; + if ($date >= $today) { + return null; + } + $toAccount = $this->findAccount('MyBank Checking Account'); + $fromAccount = $this->findAccount('Job'); + $category = Category::firstOrCreateEncrypted(['name' => 'Salary', 'user_id' => $this->user->id]); + // create journal: + + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 2, + 'transaction_currency_id' => 1, + 'description' => $description, + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + + return $journal; + + } + + /** + * @param $description + * @param Carbon $date + * @param $amount + * + * @return TransactionJournal + */ + protected function createRent($description, Carbon $date, $amount) + { + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('Land lord'); + $category = Category::firstOrCreateEncrypted(['name' => 'Rent', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'bill_id' => 1, + 'description' => $description, + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + return $journal; + + } + + /** + * @param $description + * @param Carbon $date + * @param $amount + * + * @return TransactionJournal + */ + protected function createWater($description, Carbon $date, $amount) + { + $date = new Carbon($date->format('Y-m') . '-10'); // paid on 10th + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('Vitens'); + $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => $description, + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + return $journal; + + } + + /** + * @param $description + * @param Carbon $date + * @param $amount + * + * @return TransactionJournal + */ + protected function createTV($description, Carbon $date, $amount) + { + $date = new Carbon($date->format('Y-m') . '-15'); // paid on 10th + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('XS4All'); + $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => $description, + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + return $journal; + + } + + /** + * @param $description + * @param Carbon $date + * @param $amount + * + * @return TransactionJournal + */ + protected function createPower($description, Carbon $date, $amount) + { + $date = new Carbon($date->format('Y-m') . '-06'); // paid on 10th + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('Greenchoice'); + $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => $description, + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + return $journal; + + } + + /** + * @param Carbon $date + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function createGroceries(Carbon $date) + { + $start = clone $date; + $end = clone $date; + $today = new Carbon; + $start->startOfMonth(); + $end->endOfMonth(); + + $fromAccount = $this->findAccount('MyBank Checking Account'); + $stores = ['Albert Heijn', 'PLUS', 'Bakker']; + $descriptions = ['Groceries', 'Bought some groceries', 'Got groceries']; + $category = Category::firstOrCreateEncrypted(['name' => 'Daily groceries', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]); + + $current = clone $start; + while ($current < $end && $current < $today) { + // daily groceries: + $amount = rand(1500, 2500) / 100; + $toAccount = $this->findAccount($stores[rand(0, count($stores) - 1)]); + + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => $descriptions[rand(0, count($descriptions) - 1)], + 'completed' => 1, + 'date' => $current, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + + $current->addDay(); + } + } + + /** + * @param Carbon $date + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function createDrinksAndOthers(Carbon $date) + { + $start = clone $date; + $end = clone $date; + $today = new Carbon; + $start->startOfMonth(); + $end->endOfMonth(); + $current = clone $start; + while ($current < $end && $current < $today) { + + // weekly drink: + $thisDate = clone $current; + $thisDate->addDay(); + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('Cafe Central'); + $category = Category::firstOrCreateEncrypted(['name' => 'Drinks', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Going out', 'user_id' => $this->user->id]); + $amount = rand(1500, 3600) / 100; + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => 'Going out for drinks', + 'completed' => 1, + 'date' => $thisDate, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + // shopping at some (online) shop: + + + $current->addWeek(); + } + } + + /** + * @param Carbon $date + * + * @return TransactionJournal + */ + protected function createSavings(Carbon $date) + { + $date = new Carbon($date->format('Y-m') . '-24'); // paid on 24th. + $toAccount = $this->findAccount('Savings'); + $fromAccount = $this->findAccount('MyBank Checking Account'); + $category = Category::firstOrCreateEncrypted(['name' => 'Money management', 'user_id' => $this->user->id]); + // create journal: + + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 3, + 'transaction_currency_id' => 1, + 'description' => 'Save money', + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => -150, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => 150, + + ] + ); + $journal->categories()->save($category); + + return $journal; + + } + + /** + * @param Carbon $current + * @param $name + * @param $amount + */ + protected function createBudgetLimit(Carbon $current, $name, $amount) + { + $start = clone $current; + $end = clone $current; + $budget = $this->findBudget($name); + $start->startOfMonth(); + $end->endOfMonth(); + + BudgetLimit::create( + [ + 'budget_id' => $budget->id, + 'startdate' => $start->format('Y-m-d'), + 'amount' => $amount, + 'repeats' => 0, + 'repeat_freq' => 'monthly', + ] + ); + } + + /** + * @param $name + * + * @return Budget|null + */ + protected function findBudget($name) + { + /** @var Budget $budget */ + foreach (Budget::get() as $budget) { + if ($budget->name == $name && $this->user->id == $budget->user_id) { + return $budget; + break; + } + } + + return null; + } + + /** + * @param $name + * + * @return Bill|null + */ + protected function findBill($name) + { + /** @var Bill $bill */ + foreach (Bill::get() as $bill) { + if ($bill->name == $name && $this->user->id == $bill->user_id) { + return $bill; + break; + } + } + + return null; + } + + /** + * @param $name + * + * @return Category|null + */ + protected function findCategory($name) + { + + /** @var Category $category */ + foreach (Category::get() as $category) { + if ($category->name == $name && $this->user->id == $category->user_id) { + return $category; + break; + } + } + + return null; + } + + /** + * @param $name + * + * @return PiggyBank|null + */ + protected function findPiggyBank($name) + { + + /** @var Budget $budget */ + foreach (PiggyBank::get() as $piggyBank) { + $account = $piggyBank->account()->first(); + if ($piggyBank->name == $name && $this->user->id == $account->user_id) { + return $piggyBank; + break; + } + } + + return null; + } + + /** + * @param $tagName + * + * @return Tag|null + * @internal param $tag + */ + protected function findTag($tagName) + { + /** @var Tag $tag */ + foreach (Tag::get() as $tag) { + if ($tag->tag == $tagName && $this->user->id == $tag->user_id) { + return $tag; + break; + } + } + + return null; + } + + /** + * @param $date + * + * @return static + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function createCar($date) + { + // twice: + $date = new Carbon($date->format('Y-m') . '-10'); // paid on 10th + $fromAccount = $this->findAccount('MyBank Checking Account'); + $toAccount = $this->findAccount('Shell'); + $category = Category::firstOrCreateEncrypted(['name' => 'Car', 'user_id' => $this->user->id]); + $budget = Budget::firstOrCreateEncrypted(['name' => 'Car', 'user_id' => $this->user->id]); + $amount = rand(4000, 5000) / 100; + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => 'Bought gas', + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + $journal->categories()->save($category); + $journal->budgets()->save($budget); + + // and again! + $date = new Carbon($date->format('Y-m') . '-20'); // paid on 20th + $amount = rand(4000, 5000) / 100; + + + $journal = TransactionJournal::create( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => 1, + 'transaction_currency_id' => 1, + 'description' => 'Gas for car', + 'completed' => 1, + 'date' => $date, + ] + ); + Transaction::create( + [ + 'account_id' => $fromAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1, + + ] + ); + Transaction::create( + [ + 'account_id' => $toAccount->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount, + + ] + ); + + // and again! + + return $journal; + } + + protected function createPreferences() + { + $preference = new Preference; + $preference->name = 'frontPageAccounts'; + $preference->data = [1]; + $preference->user()->associate($this->user); + $preference->save(); + } + + +} diff --git a/pu.sh b/pu.sh new file mode 100755 index 0000000000..2f20bcb843 --- /dev/null +++ b/pu.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# set testing environment +cp .env.testing .env + +# test! +if [ -z "$1" ] +then + #phpunit --verbose + phpunit +fi + +# directories to look in: +#dirs=("controllers" "database" "factories" "generators" "helpers" "models" "middleware" "repositories" "support") +# +#if [ ! -z "$1" ] +#then +# for i in "${dirs[@]}" +# do +# firstFile="./tests/$i/$1.php" +# secondFile="./tests/$i/$1Test.php" +# if [ -f "$firstFile" ] +# then +# # run it! +# phpunit --verbose $firstFile +# exit $? +# fi +# if [ -f "$secondFile" ] +# then +# # run it! +# phpunit --verbose $secondFile +# exit $? +# fi +# +# +# done +# +#fi + +# restore .env file +cp .env.local .env diff --git a/tests/TestCase.php b/tests/TestCase.php index 6dce7adf21..954bf15880 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,4 +1,5 @@ make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); return $app; } + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + public function setUp() + { + parent::setUp(); + + // if the database copy does not exist, call migrate. + $copy = storage_path('database') . '/testing-copy.db'; + $original = storage_path('database') . '/testing.db'; + + // move .env file over? + if (!file_exists($copy)) { + touch($original); + Artisan::call('migrate', ['--seed' => true]); + copy($original, $copy); + } else { + if (file_exists($copy)) { + copy($copy, $original); + } + } + // if the database copy does exists, copy back as original. + + $this->session( + [ + 'start' => Carbon::now()->startOfMonth(), + 'end' => Carbon::now()->endOfMonth(), + 'first' => Carbon::now()->startOfYear(), + ] + ); + + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + parent::tearDown(); + + // delete copy original. + //$original = __DIR__.'/../storage/database/testing.db'; + //unlink($original); + + } + + } From 493c71b89e3e33c6dbc6672d78c2e89b7a98c4a1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Jan 2016 09:16:26 +0100 Subject: [PATCH 187/276] Fix test data seeder. --- database/seeds/TestDataSeeder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index a76de1b7e0..937998ea38 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -1,5 +1,6 @@ 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]); } } From fe30850568aa19d580e577eda05050e6cd1c23af Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Jan 2016 21:32:36 +0100 Subject: [PATCH 188/276] Fixed some database migration stuff. --- app/Models/Component.php | 10 ++++++++++ database/seeds/DatabaseSeeder.php | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/Models/Component.php b/app/Models/Component.php index c7edc8dca4..3cdf904b4f 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -9,4 +9,14 @@ use Illuminate\Database\Eloquent\Model; */ class Component extends Model { + protected $fillable = ['user_id', 'name', 'class']; + + /** + * @return array + */ + public function getDates() + { + return ['created_at', 'updated_at', 'deleted_at']; + } + } diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index fa57be20b1..46ffc02a88 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -23,14 +23,14 @@ class DatabaseSeeder extends Seeder $this->call('PermissionSeeder'); // set up basic test data (as little as possible): - if (App::environment() == 'testing') { + if (App::environment() == 'testing' && gethostname() != 'lightning') { $this->call('TestDataSeeder'); } // this one is reserved for more extensive testing. - //if (App::environment() == 'testing' || App::environment() == 'homestead' || gethostname() == 'lightning') { - //$this->call('VisualTestDataSeeder'); - //} + if (App::environment() == 'testing' || gethostname() == 'lightning') { + $this->call('VisualTestDataSeeder'); + } } } From 2b6790f83faf422316187304d228dbdc5755ad1c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:07:22 +0100 Subject: [PATCH 189/276] Fixed data seeder. --- database/seeds/DatabaseSeeder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 46ffc02a88..69a846e857 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -23,12 +23,12 @@ class DatabaseSeeder extends Seeder $this->call('PermissionSeeder'); // set up basic test data (as little as possible): - if (App::environment() == 'testing' && gethostname() != 'lightning') { + if (App::environment() == 'testing') { $this->call('TestDataSeeder'); } // this one is reserved for more extensive testing. - if (App::environment() == 'testing' || gethostname() == 'lightning') { + if (App::environment() == 'local') { $this->call('VisualTestDataSeeder'); } } From b13b58a2b275d0626632f41701857150abe735c5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:18:35 +0100 Subject: [PATCH 190/276] More test data and an actual test. --- database/seeds/TestDataSeeder.php | 42 ++++++++++++++++++- tests/BasicTest.php | 1 + tests/TestCase.php | 18 ++++++++ .../Controllers/HomeControllerTest.php | 28 +++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 tests/acceptance/Controllers/HomeControllerTest.php diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 937998ea38..67e4ad7d2d 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -1,5 +1,7 @@ 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]); + $user = User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]); + $emptyUser = User::create(['email' => 'thegrumpydictator+empty@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]); + + // create asset accounts for user #1. + $this->createAssetAccounts($user); + } + + /** + * @param User $user + */ + private function createAssetAccounts(User $user) + { + $assets = ['TestData Checking Account', 'TestData Savings', 'TestData Shared', 'TestData Creditcard', 'Emergencies', 'STE']; + $ibans = ['NL47JDYU6179706202', 'NL51WGBP5832453599', 'NL81RCQZ7160379858', 'NL19NRAP2367994221', 'NL40UKBK3619908726', 'NL38SRMN4325934708']; + $assetMeta = [ + ['accountRole' => 'defaultAsset'], + ['accountRole' => 'savingAsset',], + ['accountRole' => 'sharedAsset',], + ['accountRole' => 'ccAsset', 'ccMonthlyPaymentDate' => '2015-05-27', 'ccType' => 'monthlyFull',], + ['accountRole' => 'savingAsset',], + ['accountRole' => 'savingAsset',], + ]; + + foreach ($assets as $index => $name) { + // create account: + $account = Account::create( + [ + 'user_id' => $user->id, + 'account_type_id' => 3, + 'name' => $name, + 'active' => 1, + 'encrypted' => 1, + 'iban' => $ibans[$index], + ] + ); + foreach ($assetMeta[$index] as $name => $value) { + AccountMeta::create(['account_id' => $account->id, 'name' => $name, 'data' => $value,]); + } + } } } diff --git a/tests/BasicTest.php b/tests/BasicTest.php index 079f2790df..ee499b9359 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -13,5 +13,6 @@ class BasicTest extends TestCase public function testExample() { $this->assertTrue(true); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 954bf15880..3f5e19ebcf 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,5 +1,6 @@ be($this->user()); + + $args = [ + 'start' => '2012-01-01', + 'end' => '2012-04-01', + ]; + + // if date range is > 50, should have flash. + $response = $this->call('POST', '/daterange', $args); + $this->assertEquals(200, $response->status()); + $this->assertSessionHas('warning', '91 days of data may take a while to load.'); + } + +} \ No newline at end of file From e6ac3ccfadf205c96d27d02c9794dfdb29563511 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:19:44 +0100 Subject: [PATCH 191/276] Fix travis configuration. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 861a66a8b4..0ed34d3347 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,7 @@ install: - composer install - php artisan env - mv -v .env.testing .env - - touch storage/database.sqlite - - php artisan migrate --env=testing + - touch storage/database/testing.sqlite - php artisan migrate --seed --env=testing script: From c82bdd1f5a9dba71973abfd3a332b860de89fd86 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:22:36 +0100 Subject: [PATCH 192/276] New gitignore. --- storage/database/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 storage/database/.gitignore diff --git a/storage/database/.gitignore b/storage/database/.gitignore new file mode 100755 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/storage/database/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From a49439833207fc0e377731d8fed9e2de731dd2a3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:24:06 +0100 Subject: [PATCH 193/276] Actually correct database name for once. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0ed34d3347..ffcb67ced3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ install: - composer install - php artisan env - mv -v .env.testing .env - - touch storage/database/testing.sqlite + - touch storage/database/testing.db - php artisan migrate --seed --env=testing script: From ae4cd8da1239957d1a38f45b28df1cf3f3ce5df8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:30:14 +0100 Subject: [PATCH 194/276] This should fix the travis builds. --- .travis.yml | 3 ++- tests/TestCase.php | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ffcb67ced3..fcabf8f7bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,9 @@ install: - composer install - php artisan env - mv -v .env.testing .env + - php artisan env - touch storage/database/testing.db - - php artisan migrate --seed --env=testing + - php artisan migrate --seed script: - phpunit diff --git a/tests/TestCase.php b/tests/TestCase.php index 3f5e19ebcf..b77659a6f1 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -59,8 +59,13 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase // move .env file over? if (!file_exists($copy)) { - touch($original); - Artisan::call('migrate', ['--seed' => true]); + + // maybe original does? + if (!file_exists($original)) { + touch($original); + Artisan::call('migrate', ['--seed' => true]); + } + copy($original, $copy); } else { if (file_exists($copy)) { From 173d0290ea4cc63e524978fc81e0f105eecd361d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:49:49 +0100 Subject: [PATCH 195/276] New composer stuff. --- composer.json | 6 ++++-- composer.lock | 50 ++++++++++++++++++++++++++------------------------ 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/composer.json b/composer.json index 44c5ab0766..7dbfee8eb0 100755 --- a/composer.json +++ b/composer.json @@ -55,13 +55,15 @@ ], "post-install-cmd": [ "php artisan clear-compiled", - "php artisan optimize" + "php artisan optimize", + "php artisan firefly:upgrade-instructions" ], "pre-update-cmd": [ "php artisan clear-compiled" ], "post-update-cmd": [ - "php artisan optimize" + "php artisan optimize", + "php artisan firefly:upgrade-instructions" ] }, "config": { diff --git a/composer.lock b/composer.lock index ac61669c9c..91b23f2f19 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "847987de4271f2467a4a5fc50bc04cc9", + "hash": "df00127da416acad2a36b6128b82fdd9", "content-hash": "28b178f07a713b4db441e7e1f380916e", "packages": [ { @@ -759,16 +759,16 @@ }, { "name": "laravel/framework", - "version": "v5.2.8", + "version": "v5.2.10", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "867914788c2ec942967c3608ed54626228a5f916" + "reference": "93dc5b0089eef468157fd7200e575c3861ec59a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/867914788c2ec942967c3608ed54626228a5f916", - "reference": "867914788c2ec942967c3608ed54626228a5f916", + "url": "https://api.github.com/repos/laravel/framework/zipball/93dc5b0089eef468157fd7200e575c3861ec59a5", + "reference": "93dc5b0089eef468157fd7200e575c3861ec59a5", "shasum": "" }, "require": { @@ -883,20 +883,20 @@ "framework", "laravel" ], - "time": "2016-01-12 22:45:00" + "time": "2016-01-13 20:29:10" }, { "name": "laravelcollective/html", - "version": "v5.2", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/LaravelCollective/html.git", - "reference": "5a8f1380a0d5ef21cdef37e775d86566307f8358" + "reference": "c88b2d59a56ed2290fc5082a1a6099e357c9fdbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/LaravelCollective/html/zipball/5a8f1380a0d5ef21cdef37e775d86566307f8358", - "reference": "5a8f1380a0d5ef21cdef37e775d86566307f8358", + "url": "https://api.github.com/repos/LaravelCollective/html/zipball/c88b2d59a56ed2290fc5082a1a6099e357c9fdbc", + "reference": "c88b2d59a56ed2290fc5082a1a6099e357c9fdbc", "shasum": "" }, "require": { @@ -937,20 +937,20 @@ ], "description": "HTML and Form Builders for the Laravel Framework", "homepage": "http://laravelcollective.com", - "time": "2015-12-23 07:46:46" + "time": "2016-01-16 16:54:49" }, { "name": "league/commonmark", - "version": "0.12.0", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "3eb64850ee688623db494398a5284a7a4cdf7b47" + "reference": "a4e93bc4fd1a8ff8f534040c4a07371ea5f4b484" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3eb64850ee688623db494398a5284a7a4cdf7b47", - "reference": "3eb64850ee688623db494398a5284a7a4cdf7b47", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/a4e93bc4fd1a8ff8f534040c4a07371ea5f4b484", + "reference": "a4e93bc4fd1a8ff8f534040c4a07371ea5f4b484", "shasum": "" }, "require": { @@ -962,21 +962,23 @@ }, "require-dev": { "erusev/parsedown": "~1.0", - "jgm/commonmark": "0.22", - "jgm/smartpunct": "0.22", + "jgm/commonmark": "0.24", "michelf/php-markdown": "~1.4", "mikehaertl/php-shellcommand": "~1.1.0", "phpunit/phpunit": "~4.3|~5.0", "scrutinizer/ocular": "^1.1", "symfony/finder": "~2.3" }, + "suggest": { + "league/commonmark-extras": "Library of useful extensions including smart punctuation" + }, "bin": [ "bin/commonmark" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "0.13-dev" + "dev-master": "0.14-dev" } }, "autoload": { @@ -1003,7 +1005,7 @@ "markdown", "parser" ], - "time": "2015-11-04 14:24:41" + "time": "2016-01-14 04:29:54" }, { "name": "league/csv", @@ -3897,16 +3899,16 @@ }, { "name": "symfony/class-loader", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "ec74b0a279cf3a9bd36172b3e3061591d380ce6c" + "reference": "98e9089a428ed0e39423b67352c57ef5910a3269" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/ec74b0a279cf3a9bd36172b3e3061591d380ce6c", - "reference": "ec74b0a279cf3a9bd36172b3e3061591d380ce6c", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/98e9089a428ed0e39423b67352c57ef5910a3269", + "reference": "98e9089a428ed0e39423b67352c57ef5910a3269", "shasum": "" }, "require": { @@ -3945,7 +3947,7 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2015-12-05 17:37:59" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/css-selector", From 1b3198c143ab05ac77672791c9fd80889abfaff2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:50:09 +0100 Subject: [PATCH 196/276] New: upgrade instructions when installing or upgrading composer packages. --- .../Commands/UpgradeFireflyInstructions.php | 67 +++++++++++++++++++ app/Console/Kernel.php | 2 + config/firefly.php | 2 +- config/upgrade.php | 15 +++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 app/Console/Commands/UpgradeFireflyInstructions.php create mode 100644 config/upgrade.php diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php new file mode 100644 index 0000000000..4c11f648a0 --- /dev/null +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -0,0 +1,67 @@ +line('+------------------------------------------------------------------------------+'); + $this->line(''); + + if (is_null($text)) { + $this->line('Thank you for installing Firefly III, v' . $version); + $this->info('There are no extra upgrade instructions.'); + $this->line('Firefly III should be ready for use.'); + } else { + $this->line('Thank you for installing Firefly III, v' . $version); + $this->line('Please follow these upgrade instructions carefully.'); + $this->info(wordwrap($text)); + } + + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 4cc39c0df9..386fd469c7 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -9,6 +9,7 @@ namespace FireflyIII\Console; +use FireflyIII\Console\Commands\UpgradeFireflyInstructions; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -26,6 +27,7 @@ class Kernel extends ConsoleKernel */ protected $commands = [ + UpgradeFireflyInstructions::class ]; /** diff --git a/config/firefly.php b/config/firefly.php index 952f9463e6..f37b62ce98 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -2,7 +2,7 @@ return [ 'chart' => 'chartjs', - 'version' => '3.6.1', + 'version' => '3.7.0', 'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'], 'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], 'csv_import_enabled' => true, diff --git a/config/upgrade.php b/config/upgrade.php new file mode 100644 index 0000000000..be8b331d37 --- /dev/null +++ b/config/upgrade.php @@ -0,0 +1,15 @@ + [ + '3.7.0' => 'Because of the upgrade to Laravel 5.2, several manual changes must be made to your Firefly III installation. ' . + 'Please follow the instructions on the following page: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-3.7.0'], +]; \ No newline at end of file From 20993342a27570c8f0b353f796e884565f8a9d93 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 07:55:23 +0100 Subject: [PATCH 197/276] Better wording. --- app/Console/Commands/UpgradeFireflyInstructions.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 4c11f648a0..4fe2ddea56 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -53,11 +53,13 @@ class UpgradeFireflyInstructions extends Command if (is_null($text)) { $this->line('Thank you for installing Firefly III, v' . $version); - $this->info('There are no extra upgrade instructions.'); + $this->line('If you are upgrading from a previous version,'); + $this->info('there are no extra upgrade instructions.'); $this->line('Firefly III should be ready for use.'); } else { $this->line('Thank you for installing Firefly III, v' . $version); - $this->line('Please follow these upgrade instructions carefully.'); + $this->line('If you are upgrading from a previous version,'); + $this->line('please follow these upgrade instructions carefully:'); $this->info(wordwrap($text)); } From c20a38b7b0f594e65094f166fb53039d816241e9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 08:38:19 +0100 Subject: [PATCH 198/276] Ignore file for coverage logs. --- storage/build/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 storage/build/.gitignore diff --git a/storage/build/.gitignore b/storage/build/.gitignore new file mode 100755 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/storage/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From 323c16ebe1737c3a2d7c26b1713e3211cd6d47e7 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Sun, 17 Jan 2016 15:25:47 +0100 Subject: [PATCH 199/276] Implemented additional ABNAMRO description formats --- .../Csv/Specifix/AbnAmroDescription.php | 174 +++++++++--------- 1 file changed, 86 insertions(+), 88 deletions(-) diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 890bbeff4e..5f17c62ed4 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -27,10 +27,16 @@ class AbnAmroDescription public function fix() { $this->handleAmount(); - $this->parseSepaDescription(); - + + // Try to parse the description in known formats. + $parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); + + // If the description could not be parsed, specify an unknown opposing account, as an opposing account is required + if( !$parsed ) { + $this->data[ "opposing-account-name" ] = trans('unknown'); + } + return $this->data; - } /** @@ -87,97 +93,89 @@ class AbnAmroDescription } // Add the type to the description - $this->data['description'] .= ' (' . $type . ')'; + if( $type ) + $this->data['description'] .= ' (' . $type . ')'; return true; } return false; } + + /** + * Parses the current description in TRTP format + * @return boolean true if the description is TRTP format, false otherwise + */ + protected function parseTRTPDescription() + { + // See if the current description is formatted in TRTP format + if( preg_match_all( "!\/([A-Z]{3,4})\/([^/]*)!", $this->data[ "description" ], $matches, PREG_SET_ORDER ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as TRTP format.'); -/*** - * -def ParseDescription(desc): - values = None - ### SEPA PLAIN: SEPA iDEAL IBAN: NL12RABO0121212212 BIC: RABONL2U Naam: Silver Ocean B.V. Omschrijving: 1232138 1232131233 412321 iBOOD.com iBOOD.com B.V. Kenmerk: 12-12-2014 21:03 002000 0213123238 - sepa = re.findall(r"(?P^SEPA.{28})", desc, re.I) - if (sepa): - values = {} - value = sepa[0] - values["TRTP"] = value.strip() - values["EREF"] = "" - values["REMI"] = "" - sepa = re.findall(r"(?P[A-Za-z]+(?=:\s)):\s(?P[A-Za-z 0-9.-]+(?=\s))", desc, re.I) - for line in sepa: - key = line[0] - if key.upper() == 'OMSCHRIJVING': - key = 'REMI' - if key.upper() == 'KENMERK': - key = 'EREF' - if key.upper() == 'NAAM': - key = 'NAME' - value = line[1] - values[key] = value.strip() - # print (values) - # continue - if len(values["REMI"]) > 19: - values["REMI"] = values["REMI"][0:18] + values["REMI"][19:] - if values["REMI"] == "": - values["REMI"] = values["TRTP"] - - ### TRTP ENCODED: /TRTP/SEPA OVERBOEKING/IBAN/NL23ABNA0000000000/BIC/ABNANL2A/NAME/baasd dsdsT CJ/REMI/Nullijn/EREF/NOTPROVIDED - trtp = re.findall(r"\/(?P[A-Z]{3,4})\/(?P.*?(?:(?=\/[A-Z]{3,4}\/)|$))",desc, re.I) - if (trtp): - values = {} - values["EREF"] = "" - values["REMI"] = "" - for line in trtp: - key = line[0] - value = line[1] - values[key] = value.strip() - # print (values) - # continue - if values["REMI"] == "": - values["REMI"] = values["TRTP"] - - ### BEA: BEA NR:00AJ01 31.01.01/19.54 Van HarenSchoenen132 UDE,PAS333 - trtp = re.findall(r"(?P[BG]EA) +(?PNR:[a-zA-Z:0-9]+) +(?P[0-9.\/]+) +(?P[^,]*)", desc, re.I) - if (trtp): - values = {} - values["TRTP"] = str(trtp[0][0]).strip() - values["NAME"] = str(trtp[0][3]).strip() - values["EREF"] = str(trtp[0][1]).strip() - values["DATE"] = str(trtp[0][2]).strip() - values["REMI"] = values["TRTP"] + " " + values["NAME"] - # print (values) - # continue - - ### OLD: 12.21.22.222 BNP aaaaaaa aaaaaa SCH BETALINGSKENM. 2323233232323323 MAAND* APRIL 01 REF* 1212121-42-41 - trtp = re.findall(r"^ ?(?P[0-9.]{12,15})\W+(?P.{32})", desc, re.I) - if (trtp): - values = {} - values["TRTP"] = "OLD" - values["IBAN"] = str(trtp[0][0]).strip() - values["NAME"] = str(trtp[0][1]).strip() - values["EREF"] = "" - values["REMI"] = values["TRTP"] + " " + values["NAME"] - # print (values) - # continue - ### ABN AMRO Bank N.V. Prive pakket 3,25 - abn = re.findall(r"^ABN AMRO.{24} (?P.*)", desc, re.I) - if (abn): - values = {} - values["TRTP"] = "ABN AMBRO" - values["NAME"] = "ABN AMBRO" - values["EREF"] = str(abn[0]).strip() - values["REMI"] = values["EREF"] - # print (values) - # continue - if (values == None): - # print ("Unkown: ### %s ###" % ( desc )) - return None - return values * - * - */ + foreach( $matches as $match ) { + $key = $match[1]; + $value = trim($match[2]); + + switch( strtoupper($key) ) { + case 'TRTP': + $type = $value; + break; + case 'NAME': + $this->data['opposing-account-name'] = $value; + break; + case 'REMI': + $this->data['description'] = $value; + break; + case 'IBAN': + $this->data['opposing-account-iban'] = $value; + break; + default: + // Ignore the rest + } + } + + // Add the type to the description + if( $type ) + $this->data['description'] .= ' (' . $type . ')'; + + return true; + } + + return false; + } + /** + * Parses the current description in GEA/BEA format + * @return boolean true if the description is GEA/BEAformat, false otherwise + */ + protected function parseGEABEADescription() + { + // See if the current description is formatted in GEA/BEA format + if( preg_match( "/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/", $this->data[ "description" ], $matches ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.'); + + $this->data[ "opposing-account-name" ] = $matches[4]; + $this->data[ "description" ] = $matches[4] . " (" . $matches[1] . ")"; + } + + return false; + } + + /** + * Parses the current description with costs from ABN AMRO itself + * @return boolean true if the description is GEA/BEAformat, false otherwise + */ + protected function parseABNAMRODescription() + { + // See if the current description is formatted in ABN AMRO format + if( preg_match( "/ABN AMRO.{24} (.*)/", $this->data[ "description" ], $matches ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.'); + + $this->data[ "opposing-account-name" ] = "ABN AMRO"; + $this->data[ "description" ] = $matches[1]; + } + + return false; + } + } From 68cdfd00b041cac3c53bf67e8f762dbb84ffc49a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 15:28:01 +0100 Subject: [PATCH 200/276] Update test things. --- app/Models/TransactionType.php | 2 ++ phpunit.xml | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index e6778eb8cb..f3201469c1 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -59,6 +59,8 @@ class TransactionType extends Model } /** + * @codeCoverageIgnore + * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function transactionJournals() diff --git a/phpunit.xml b/phpunit.xml index cc0841c1d3..04f27f97f4 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -18,6 +18,14 @@ app/ + + From 4909fcc8b4bb744d2dceb74065dbec3a6ca43c80 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Sun, 17 Jan 2016 15:37:49 +0100 Subject: [PATCH 201/276] Moved parsing of amount with decimal separator to Converter object --- app/Helpers/Csv/Converter/AmountComma.php | 30 +++++++++++++++++++ .../Csv/Specifix/AbnAmroDescription.php | 6 ---- config/csv.php | 6 ++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 app/Helpers/Csv/Converter/AmountComma.php diff --git a/app/Helpers/Csv/Converter/AmountComma.php b/app/Helpers/Csv/Converter/AmountComma.php new file mode 100644 index 0000000000..2427361148 --- /dev/null +++ b/app/Helpers/Csv/Converter/AmountComma.php @@ -0,0 +1,30 @@ +value ); + + if (is_numeric($value)) { + return floatval($value); + } + + return 0; + } +} diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 5f17c62ed4..6a422a5a24 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -26,8 +26,6 @@ class AbnAmroDescription */ public function fix() { - $this->handleAmount(); - // Try to parse the description in known formats. $parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); @@ -55,10 +53,6 @@ class AbnAmroDescription $this->row = $row; } - protected function handleAmount() { - $this->data['amount'] = floatval(str_replace(',', '.', $this->row[6])); - } - /** * Parses the current description in SEPA format * @return boolean true if the description is SEPA format, false otherwise diff --git a/config/csv.php b/config/csv.php index d8cbd4daa5..dfdcc3d26f 100644 --- a/config/csv.php +++ b/config/csv.php @@ -177,6 +177,12 @@ return [ 'converter' => 'Amount', 'field' => 'amount', ], + 'amount-comma-separated' => [ + 'name' => 'Amount (comma as decimal separator)', + 'mappable' => false, + 'converter' => 'AmountComma', + 'field' => 'amount', + ], 'sepa-ct-id' => [ 'name' => 'SEPA Credit Transfer end-to-end ID', 'mappable' => false, From e6db49c20cd1389557384e8e966494d11ad2fc92 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jan 2016 15:48:18 +0100 Subject: [PATCH 202/276] Some tests. --- app/Http/Middleware/Range.php | 4 ++ cover.sh | 46 +++++++++++++++++++ phpunit.cover.xml | 41 +++++++++++++++++ phpunit.default.xml | 35 ++++++++++++++ .../Controllers/HomeControllerTest.php | 16 +++++++ 5 files changed, 142 insertions(+) create mode 100755 cover.sh create mode 100755 phpunit.cover.xml create mode 100755 phpunit.default.xml diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index a4b6ead33d..36155e56d3 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -73,6 +73,10 @@ class Range Session::put('first', Carbon::now()->startOfYear()); } } + + // check "sum of everything". + + $current = Carbon::now()->formatLocalized('%B %Y'); $next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y'); $prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y'); diff --git a/cover.sh b/cover.sh new file mode 100755 index 0000000000..4311932be6 --- /dev/null +++ b/cover.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# set testing environment +cp .env.testing .env + +# set cover: +cp phpunit.cover.xml phpunit.xml + +# test! +if [ -z "$1" ] +then + phpdbg -qrr /usr/local/bin/phpunit +fi + +# directories to look in: +#dirs=("controllers" "database" "factories" "generators" "helpers" "models" "middleware" "repositories" "support") +# +#if [ ! -z "$1" ] +#then +# for i in "${dirs[@]}" +# do +# firstFile="./tests/$i/$1.php" +# secondFile="./tests/$i/$1Test.php" +# if [ -f "$firstFile" ] +# then +# # run it! +# phpunit --verbose $firstFile +# exit $? +# fi +# if [ -f "$secondFile" ] +# then +# # run it! +# phpunit --verbose $secondFile +# exit $? +# fi +# +# +# done +# +#fi + +# restore .env file +cp .env.local .env + +# restore cover +cp phpunit.default.xml phpunit.xml \ No newline at end of file diff --git a/phpunit.cover.xml b/phpunit.cover.xml new file mode 100755 index 0000000000..05acf49856 --- /dev/null +++ b/phpunit.cover.xml @@ -0,0 +1,41 @@ + + + + + + + ./tests/ + + + + + app/ + + + + + + + + + + + + + diff --git a/phpunit.default.xml b/phpunit.default.xml new file mode 100755 index 0000000000..04f27f97f4 --- /dev/null +++ b/phpunit.default.xml @@ -0,0 +1,35 @@ + + + + + ./tests/ + + + + + app/ + + + + + + + + + + + diff --git a/tests/acceptance/Controllers/HomeControllerTest.php b/tests/acceptance/Controllers/HomeControllerTest.php index 23d725bb8e..804a9371ad 100644 --- a/tests/acceptance/Controllers/HomeControllerTest.php +++ b/tests/acceptance/Controllers/HomeControllerTest.php @@ -17,6 +17,7 @@ class HomeControllerTest extends TestCase $args = [ 'start' => '2012-01-01', 'end' => '2012-04-01', + '_token' => Session::token(), ]; // if date range is > 50, should have flash. @@ -25,4 +26,19 @@ class HomeControllerTest extends TestCase $this->assertSessionHas('warning', '91 days of data may take a while to load.'); } + public function testFlush() + { + $this->be($this->user()); + $response = $this->call('GET', '/flush'); + $this->assertEquals(302, $response->status()); + } + + public function testIndex() + { + $this->be($this->user()); + $response = $this->call('GET', '/'); + $this->assertEquals(200, $response->status()); + } + + } \ No newline at end of file From cbe1f762ca2e9ec88c9ff009595089f2d28a01b9 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Mon, 18 Jan 2016 09:09:30 +0100 Subject: [PATCH 203/276] Fixing a bug with expense account being set to unknown --- app/Helpers/Csv/Specifix/AbnAmroDescription.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 6a422a5a24..49295b28e9 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -150,6 +150,8 @@ class AbnAmroDescription $this->data[ "opposing-account-name" ] = $matches[4]; $this->data[ "description" ] = $matches[4] . " (" . $matches[1] . ")"; + + return true; } return false; @@ -167,6 +169,8 @@ class AbnAmroDescription $this->data[ "opposing-account-name" ] = "ABN AMRO"; $this->data[ "description" ] = $matches[1]; + + return true; } return false; From cc712b0c75535f1cb9dd5027c8a1f86d6ac93a30 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Mon, 18 Jan 2016 12:31:45 +0100 Subject: [PATCH 204/276] Updated message identifier for unknown opposing account --- app/Helpers/Csv/Specifix/AbnAmroDescription.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 49295b28e9..6e6dc4f02a 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -29,9 +29,10 @@ class AbnAmroDescription // Try to parse the description in known formats. $parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); - // If the description could not be parsed, specify an unknown opposing account, as an opposing account is required + // If the description could not be parsed, specify an unknown opposing + // account, as an opposing account is required if( !$parsed ) { - $this->data[ "opposing-account-name" ] = trans('unknown'); + $this->data[ "opposing-account-name" ] = trans('firefly.unknown'); } return $this->data; From bd5d73d1e6693fbb67dc83837370219337ed9204 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Mon, 18 Jan 2016 12:37:06 +0100 Subject: [PATCH 205/276] Removed transaction type from the description --- app/Helpers/Csv/Specifix/AbnAmroDescription.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 6e6dc4f02a..75d708e39e 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -87,10 +87,6 @@ class AbnAmroDescription } } - // Add the type to the description - if( $type ) - $this->data['description'] .= ' (' . $type . ')'; - return true; } @@ -129,10 +125,6 @@ class AbnAmroDescription } } - // Add the type to the description - if( $type ) - $this->data['description'] .= ' (' . $type . ')'; - return true; } @@ -150,7 +142,7 @@ class AbnAmroDescription Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.'); $this->data[ "opposing-account-name" ] = $matches[4]; - $this->data[ "description" ] = $matches[4] . " (" . $matches[1] . ")"; + $this->data[ "description" ] = $matches[4]; return true; } From be5ff35b13e74c9f9124bedd62d494f114a37eeb Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 18 Jan 2016 13:15:11 +0100 Subject: [PATCH 206/276] Reversed the removal of the validating trait since @roberthorlings discovered it's actually still being used. --- app/Models/TransactionJournal.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 4299f900b1..c69f4c02b7 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\TransactionJournal @@ -54,13 +55,24 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class TransactionJournal extends Model { - use SoftDeletes; + use SoftDeletes, ValidatingTrait; protected $fillable = ['user_id', 'transaction_type_id', 'bill_id', 'transaction_currency_id', 'description', 'completed', 'date', 'encrypted', 'tag_count']; protected $hidden = ['encrypted']; protected $dates = ['created_at', 'updated_at', 'date', 'deleted_at']; + protected $rules + = [ + 'user_id' => 'required|exists:users,id', + 'transaction_type_id' => 'required|exists:transaction_types,id', + 'bill_id' => 'exists:bills,id', + 'transaction_currency_id' => 'required|exists:transaction_currencies,id', + 'description' => 'required|between:1,1024', + 'completed' => 'required|boolean', + 'date' => 'required|date', + 'encrypted' => 'required|boolean', + ]; /** * @codeCoverageIgnore From 10ea60daaffda541a05aa3b2812565fd1dd493b2 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Mon, 18 Jan 2016 15:42:56 +0100 Subject: [PATCH 207/276] Improved SEPA description parsing --- app/Helpers/Csv/Specifix/AbnAmroDescription.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 75d708e39e..2b6b3c97d5 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -28,7 +28,7 @@ class AbnAmroDescription { // Try to parse the description in known formats. $parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); - + // If the description could not be parsed, specify an unknown opposing // account, as an opposing account is required if( !$parsed ) { @@ -66,7 +66,7 @@ class AbnAmroDescription $type = trim($matches[1]); // SEPA plain descriptions contain several key-value pairs, split by a colon - preg_match_all( "/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9.-]+(?=\s))/", $this->data[ "description" ], $matches, PREG_SET_ORDER ); + preg_match_all( "/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s))/", $this->data[ "description" ], $matches, PREG_SET_ORDER ); foreach( $matches as $match ) { $key = $match[1]; From f39f25760a81af7b3bc4b9db05b5e84906f539f5 Mon Sep 17 00:00:00 2001 From: Robert Horlings Date: Mon, 18 Jan 2016 15:43:30 +0100 Subject: [PATCH 208/276] Added messages for new specifix --- resources/lang/en_US/firefly.php | 2 ++ resources/lang/nl_NL/firefly.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index be816a7d26..d30005be04 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -257,6 +257,7 @@ return [ 'csv_column_account-id' => 'Asset account ID (matching Firefly)', 'csv_column_account-name' => 'Asset account (name)', 'csv_column_amount' => 'Amount', + 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', 'csv_column_bill-id' => 'Bill ID (matching Firefly)', 'csv_column_bill-name' => 'Bill name', 'csv_column_budget-id' => 'Budget ID (matching Firefly)', @@ -280,6 +281,7 @@ return [ 'csv_column_tags-comma' => 'Tags (comma separated)', 'csv_column_tags-space' => 'Tags (space separated)', 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', + 'csv_specifix_AbnAmroDescription' => 'Select this when you\'re importing ABN AMRO CSV export files.', 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 3493ef0643..a2fc14f54e 100755 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -257,6 +257,7 @@ return [ 'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', 'csv_column_account-name' => 'Betaalrekeningnaam', 'csv_column_amount' => 'Bedrag', + 'csv_column_amount-comma-separated' => 'Bedrag (komma as decimaalscheidingsteken)', 'csv_column_bill-id' => 'Contract (ID gelijk aan Firefly)', 'csv_column_bill-name' => 'Contractnaam', 'csv_column_budget-id' => 'Budget (ID gelijk aan Firefly)', @@ -280,6 +281,7 @@ return [ 'csv_column_tags-comma' => 'Tags (kommagescheiden)', 'csv_column_tags-space' => 'Tags (spatiegescheiden)', 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', + 'csv_specifix_AbnAmroDescription' => 'Vink dit aan als je ABN AMRO CSV bestanden importeert.', 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', 'csv_delimiter_help' => 'Kies het veldscheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', From eed3d021d91070f8f0e8958a988e0d8135f6b4ca Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 08:48:38 +0100 Subject: [PATCH 209/276] Small cleanup. --- app/Helpers/Csv/Converter/Amount.php | 4 +--- app/Helpers/Csv/Converter/AmountComma.php | 4 +--- app/Helpers/Csv/Converter/BudgetId.php | 2 +- app/Helpers/Csv/Converter/CurrencyCode.php | 2 +- app/Helpers/Csv/Converter/CurrencyId.php | 2 +- app/Helpers/Csv/Converter/CurrencyName.php | 2 +- app/Helpers/Csv/Converter/CurrencySymbol.php | 2 +- app/Helpers/Csv/Converter/Date.php | 2 +- app/Helpers/Csv/Converter/Description.php | 2 +- app/Helpers/Csv/Converter/Ignore.php | 4 +--- app/Helpers/Csv/Converter/OpposingAccountId.php | 2 +- app/Helpers/Csv/Converter/OpposingAccountName.php | 2 +- app/Helpers/Csv/Converter/RabobankDebetCredit.php | 2 +- app/Helpers/Csv/Converter/TagsComma.php | 3 +-- app/Helpers/Csv/Converter/TagsSpace.php | 3 +-- 15 files changed, 15 insertions(+), 23 deletions(-) diff --git a/app/Helpers/Csv/Converter/Amount.php b/app/Helpers/Csv/Converter/Amount.php index bbad0932a0..7ec25ce17e 100644 --- a/app/Helpers/Csv/Converter/Amount.php +++ b/app/Helpers/Csv/Converter/Amount.php @@ -2,8 +2,6 @@ namespace FireflyIII\Helpers\Csv\Converter; -use FireflyIII\Models\Account; - /** * Class Amount * @@ -13,7 +11,7 @@ class Amount extends BasicConverter implements ConverterInterface { /** - * @return Account|null + * @return string|int */ public function convert() { diff --git a/app/Helpers/Csv/Converter/AmountComma.php b/app/Helpers/Csv/Converter/AmountComma.php index 2427361148..73f067f7aa 100644 --- a/app/Helpers/Csv/Converter/AmountComma.php +++ b/app/Helpers/Csv/Converter/AmountComma.php @@ -2,8 +2,6 @@ namespace FireflyIII\Helpers\Csv\Converter; -use FireflyIII\Models\Account; - /** * Class AmountComma * @@ -15,7 +13,7 @@ class AmountComma extends BasicConverter implements ConverterInterface { /** - * @return Account|null + * @return float|int */ public function convert() { diff --git a/app/Helpers/Csv/Converter/BudgetId.php b/app/Helpers/Csv/Converter/BudgetId.php index 1025a289a2..5fa10d1fb0 100644 --- a/app/Helpers/Csv/Converter/BudgetId.php +++ b/app/Helpers/Csv/Converter/BudgetId.php @@ -5,7 +5,7 @@ use Auth; use FireflyIII\Models\Budget; /** - * Class AccountId + * Class BudgetId * * @package FireflyIII\Helpers\Csv\Converter */ diff --git a/app/Helpers/Csv/Converter/CurrencyCode.php b/app/Helpers/Csv/Converter/CurrencyCode.php index 598bd4bd5e..c17d675457 100644 --- a/app/Helpers/Csv/Converter/CurrencyCode.php +++ b/app/Helpers/Csv/Converter/CurrencyCode.php @@ -13,7 +13,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface { /** - * @return mixed|static + * @return TransactionCurrency */ public function convert() { diff --git a/app/Helpers/Csv/Converter/CurrencyId.php b/app/Helpers/Csv/Converter/CurrencyId.php index f620711bd1..25bf449850 100644 --- a/app/Helpers/Csv/Converter/CurrencyId.php +++ b/app/Helpers/Csv/Converter/CurrencyId.php @@ -13,7 +13,7 @@ class CurrencyId extends BasicConverter implements ConverterInterface { /** - * @return mixed|static + * @return TransactionCurrency */ public function convert() { diff --git a/app/Helpers/Csv/Converter/CurrencyName.php b/app/Helpers/Csv/Converter/CurrencyName.php index 549cb922f2..95c57e7d31 100644 --- a/app/Helpers/Csv/Converter/CurrencyName.php +++ b/app/Helpers/Csv/Converter/CurrencyName.php @@ -13,7 +13,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface { /** - * @return mixed|static + * @return TransactionCurrency */ public function convert() { diff --git a/app/Helpers/Csv/Converter/CurrencySymbol.php b/app/Helpers/Csv/Converter/CurrencySymbol.php index c21617dc73..97184fdfb0 100644 --- a/app/Helpers/Csv/Converter/CurrencySymbol.php +++ b/app/Helpers/Csv/Converter/CurrencySymbol.php @@ -13,7 +13,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface { /** - * @return mixed|static + * @return TransactionCurrency */ public function convert() { diff --git a/app/Helpers/Csv/Converter/Date.php b/app/Helpers/Csv/Converter/Date.php index fa6a6a8cb4..d2487ccd24 100644 --- a/app/Helpers/Csv/Converter/Date.php +++ b/app/Helpers/Csv/Converter/Date.php @@ -17,7 +17,7 @@ class Date extends BasicConverter implements ConverterInterface { /** - * @return static + * @return Carbon * @throws FireflyException */ public function convert() diff --git a/app/Helpers/Csv/Converter/Description.php b/app/Helpers/Csv/Converter/Description.php index dfd704c25f..bb5626e251 100644 --- a/app/Helpers/Csv/Converter/Description.php +++ b/app/Helpers/Csv/Converter/Description.php @@ -12,7 +12,7 @@ class Description extends BasicConverter implements ConverterInterface /** - * @return mixed + * @return string */ public function convert() { diff --git a/app/Helpers/Csv/Converter/Ignore.php b/app/Helpers/Csv/Converter/Ignore.php index d7175841bd..a9dff94088 100644 --- a/app/Helpers/Csv/Converter/Ignore.php +++ b/app/Helpers/Csv/Converter/Ignore.php @@ -2,8 +2,6 @@ namespace FireflyIII\Helpers\Csv\Converter; -use FireflyIII\Models\Account; - /** * Class Amount * @@ -13,7 +11,7 @@ class Ignore extends BasicConverter implements ConverterInterface { /** - * @return Account|null + * @return null */ public function convert() { diff --git a/app/Helpers/Csv/Converter/OpposingAccountId.php b/app/Helpers/Csv/Converter/OpposingAccountId.php index f0379d57b3..48d0cbe6e0 100644 --- a/app/Helpers/Csv/Converter/OpposingAccountId.php +++ b/app/Helpers/Csv/Converter/OpposingAccountId.php @@ -6,7 +6,7 @@ use Auth; use FireflyIII\Models\Account; /** - * Class OpposingName + * Class OpposingAccountId * * @package FireflyIII\Helpers\Csv\Converter */ diff --git a/app/Helpers/Csv/Converter/OpposingAccountName.php b/app/Helpers/Csv/Converter/OpposingAccountName.php index 01efa1c8fd..ef9e9ad97c 100644 --- a/app/Helpers/Csv/Converter/OpposingAccountName.php +++ b/app/Helpers/Csv/Converter/OpposingAccountName.php @@ -6,7 +6,7 @@ use Auth; use FireflyIII\Models\Account; /** - * Class OpposingName + * Class OpposingAccountName * * @package FireflyIII\Helpers\Csv\Converter */ diff --git a/app/Helpers/Csv/Converter/RabobankDebetCredit.php b/app/Helpers/Csv/Converter/RabobankDebetCredit.php index 933bc9e245..1861f6e172 100644 --- a/app/Helpers/Csv/Converter/RabobankDebetCredit.php +++ b/app/Helpers/Csv/Converter/RabobankDebetCredit.php @@ -13,7 +13,7 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface /** - * @return mixed + * @return int */ public function convert() { diff --git a/app/Helpers/Csv/Converter/TagsComma.php b/app/Helpers/Csv/Converter/TagsComma.php index b854d2a09f..9356856294 100644 --- a/app/Helpers/Csv/Converter/TagsComma.php +++ b/app/Helpers/Csv/Converter/TagsComma.php @@ -3,7 +3,6 @@ namespace FireflyIII\Helpers\Csv\Converter; use Auth; -use FireflyIII\Models\Bill; use FireflyIII\Models\Tag; use Illuminate\Support\Collection; @@ -16,7 +15,7 @@ class TagsComma extends BasicConverter implements ConverterInterface { /** - * @return Bill + * @return Collection */ public function convert() { diff --git a/app/Helpers/Csv/Converter/TagsSpace.php b/app/Helpers/Csv/Converter/TagsSpace.php index 14d91a34f8..323f6005cc 100644 --- a/app/Helpers/Csv/Converter/TagsSpace.php +++ b/app/Helpers/Csv/Converter/TagsSpace.php @@ -3,7 +3,6 @@ namespace FireflyIII\Helpers\Csv\Converter; use Auth; -use FireflyIII\Models\Bill; use FireflyIII\Models\Tag; use Illuminate\Support\Collection; @@ -16,7 +15,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface { /** - * @return Bill + * @return Collection */ public function convert() { From 26fb03e6c85e5fc84c7271a847a2ddf2d24a554d Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 09:11:15 +0100 Subject: [PATCH 210/276] Reformatted code according to scrutinizer-ci. --- app/Helpers/Csv/Converter/AmountComma.php | 6 +- .../Csv/Specifix/AbnAmroDescription.php | 87 ++++++++++--------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/app/Helpers/Csv/Converter/AmountComma.php b/app/Helpers/Csv/Converter/AmountComma.php index 73f067f7aa..5fed5587b9 100644 --- a/app/Helpers/Csv/Converter/AmountComma.php +++ b/app/Helpers/Csv/Converter/AmountComma.php @@ -4,7 +4,7 @@ namespace FireflyIII\Helpers\Csv\Converter; /** * Class AmountComma - * + * * Parses the input as the amount with a comma as decimal separator * * @package FireflyIII\Helpers\Csv\Converter @@ -17,8 +17,8 @@ class AmountComma extends BasicConverter implements ConverterInterface */ public function convert() { - $value = str_replace(",", ".", $this->value ); - + $value = str_replace(',', '.', $this->value); + if (is_numeric($value)) { return floatval($value); } diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index 2b6b3c97d5..ecdc75b8bb 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -5,8 +5,8 @@ namespace FireflyIII\Helpers\Csv\Specifix; use Log; /** - * Parses the description from txt files for ABN AMRO bank accounts. - * + * Parses the description from txt files for ABN AMRO bank accounts. + * * Based on the logic as described in the following Gist: * https://gist.github.com/vDorst/68d555a6a90f62fec004 * @@ -31,10 +31,10 @@ class AbnAmroDescription // If the description could not be parsed, specify an unknown opposing // account, as an opposing account is required - if( !$parsed ) { - $this->data[ "opposing-account-name" ] = trans('firefly.unknown'); + if (!$parsed) { + $this->data['opposing-account-name'] = trans('firefly.unknown'); } - + return $this->data; } @@ -53,7 +53,7 @@ class AbnAmroDescription { $this->row = $row; } - + /** * Parses the current description in SEPA format * @return boolean true if the description is SEPA format, false otherwise @@ -61,18 +61,17 @@ class AbnAmroDescription protected function parseSepaDescription() { // See if the current description is formatted as a SEPA plain description - if( preg_match( "/^SEPA(.{28})/", $this->data[ "description" ], $matches ) ) { + if (preg_match('/^SEPA(.{28})/', $this->data['description'], $matches)) { Log::debug('AbnAmroSpecifix: Description is structured as SEPA plain description.'); - $type = trim($matches[1]); - + // SEPA plain descriptions contain several key-value pairs, split by a colon - preg_match_all( "/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s))/", $this->data[ "description" ], $matches, PREG_SET_ORDER ); - - foreach( $matches as $match ) { - $key = $match[1]; + preg_match_all('/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s))/', $this->data['description'], $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + $key = $match[1]; $value = trim($match[2]); - - switch( strtoupper($key) ) { + + switch (strtoupper($key)) { case 'OMSCHRIJVING': $this->data['description'] = $value; break; @@ -86,10 +85,10 @@ class AbnAmroDescription // Ignore the rest } } - + return true; } - + return false; } @@ -100,17 +99,18 @@ class AbnAmroDescription protected function parseTRTPDescription() { // See if the current description is formatted in TRTP format - if( preg_match_all( "!\/([A-Z]{3,4})\/([^/]*)!", $this->data[ "description" ], $matches, PREG_SET_ORDER ) ) { + if (preg_match_all('!\/([A-Z]{3,4})\/([^/]*)!', $this->data['description'], $matches, PREG_SET_ORDER)) { Log::debug('AbnAmroSpecifix: Description is structured as TRTP format.'); - - foreach( $matches as $match ) { - $key = $match[1]; + + foreach ($matches as $match) { + $key = $match[1]; $value = trim($match[2]); - - switch( strtoupper($key) ) { - case 'TRTP': - $type = $value; - break; + + switch (strtoupper($key)) { + // is not being used. +// case 'TRTP': +// $type = $value; +// break; case 'NAME': $this->data['opposing-account-name'] = $value; break; @@ -124,10 +124,10 @@ class AbnAmroDescription // Ignore the rest } } - + return true; } - + return false; } @@ -138,35 +138,36 @@ class AbnAmroDescription protected function parseGEABEADescription() { // See if the current description is formatted in GEA/BEA format - if( preg_match( "/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/", $this->data[ "description" ], $matches ) ) { + if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) { Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.'); - - $this->data[ "opposing-account-name" ] = $matches[4]; - $this->data[ "description" ] = $matches[4]; - + + // description and opposing account will be the same. + $this->data['opposing-account-name'] = $matches[4]; + $this->data['description'] = $matches[4]; + return true; } - + return false; } - + /** * Parses the current description with costs from ABN AMRO itself - * @return boolean true if the description is GEA/BEAformat, false otherwise + * @return boolean true if the description is GEA/BEA-format, false otherwise */ protected function parseABNAMRODescription() { // See if the current description is formatted in ABN AMRO format - if( preg_match( "/ABN AMRO.{24} (.*)/", $this->data[ "description" ], $matches ) ) { + if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) { Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.'); - - $this->data[ "opposing-account-name" ] = "ABN AMRO"; - $this->data[ "description" ] = $matches[1]; - + + $this->data['opposing-account-name'] = "ABN AMRO"; + $this->data['description'] = $matches[1]; + return true; } - + return false; } - + } From 9360eb6a70608133c1a2befc18796d24698b23df Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 13:53:55 +0100 Subject: [PATCH 211/276] First account controller test. --- .../Controllers/AccountControllerTest.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/acceptance/Controllers/AccountControllerTest.php diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php new file mode 100644 index 0000000000..e1942c0e70 --- /dev/null +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -0,0 +1,65 @@ +be($this->user()); + $response = $this->call('GET', '/accounts/create/asset'); + $this->assertEquals(200, $response->status()); + } + + public function testDelete() + { + $this->be($this->user()); + $response = $this->call('GET', '/accounts/delete/1'); + $this->assertEquals(200, $response->status()); + } + + public function testDestroy() + { + $this->markTestIncomplete(); + } + + public function testEdit() + { + $this->be($this->user()); + $response = $this->call('GET', '/accounts/edit/1'); + $this->assertEquals(200, $response->status()); + } + + public function testIndex() + { + $this->be($this->user()); + $response = $this->call('GET', '/accounts/asset'); + $this->assertEquals(200, $response->status()); + } + + public function testShow() + { + $this->be($this->user()); + $response = $this->call('GET', '/accounts/show/1'); + $this->assertEquals(200, $response->status()); + } + + public function testStore() + { + $this->markTestIncomplete(); + } + + public function testUpdate() + { + $this->markTestIncomplete(); + } + +} From f3fff6f1c506bdb5e6f53190fb22b8748254f9b4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 13:59:54 +0100 Subject: [PATCH 212/276] Code rearrangement. --- .../Commands/UpgradeFireflyInstructions.php | 13 +- app/Console/Kernel.php | 2 +- .../Chart/Account/AccountChartGenerator.php | 18 +- .../Account/ChartJsAccountChartGenerator.php | 31 +- .../Budget/ChartJsBudgetChartGenerator.php | 68 ++-- app/Helpers/Attachments/AttachmentHelper.php | 70 ++-- .../Attachments/AttachmentHelperInterface.php | 12 +- app/Helpers/Csv/Data.php | 274 ++++++------- app/Helpers/Csv/Importer.php | 274 +++++++------ .../Csv/Specifix/AbnAmroDescription.php | 88 ++-- app/Helpers/Csv/Wizard.php | 22 +- app/Helpers/Report/ReportHelper.php | 384 +++++++++--------- app/Helpers/Report/ReportHelperInterface.php | 18 +- app/Helpers/Report/ReportQuery.php | 138 +++---- .../Account/AccountRepository.php | 99 +++-- .../Account/AccountRepositoryInterface.php | 42 +- .../RuleGroup/RuleGroupRepository.php | 57 ++- .../RuleGroupRepositoryInterface.php | 17 +- 18 files changed, 805 insertions(+), 822 deletions(-) diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 4fe2ddea56..a61ffaee7b 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -12,19 +12,18 @@ use Illuminate\Console\Command; */ class UpgradeFireflyInstructions extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'firefly:upgrade-instructions'; - /** * The console command description. * * @var string */ protected $description = 'Command description'; + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'firefly:upgrade-instructions'; /** * Create a new command instance. diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 386fd469c7..88960c8a36 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -27,7 +27,7 @@ class Kernel extends ConsoleKernel */ protected $commands = [ - UpgradeFireflyInstructions::class + UpgradeFireflyInstructions::class, ]; /** diff --git a/app/Generator/Chart/Account/AccountChartGenerator.php b/app/Generator/Chart/Account/AccountChartGenerator.php index 5969d86178..5601e86306 100644 --- a/app/Generator/Chart/Account/AccountChartGenerator.php +++ b/app/Generator/Chart/Account/AccountChartGenerator.php @@ -14,6 +14,15 @@ use Illuminate\Support\Collection; interface AccountChartGenerator { + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end); + /** * @param Collection $accounts * @param Carbon $start @@ -31,13 +40,4 @@ interface AccountChartGenerator * @return array */ public function single(Account $account, Carbon $start, Carbon $end); - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end); } diff --git a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php index ef53c4b7bd..b9807954c5 100644 --- a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php +++ b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php @@ -62,22 +62,6 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator return $data; } - /** - * @param $array - * @param $entryId - * - * @return string - */ - protected function isInArray($array, $entryId) - { - if (isset($array[$entryId])) { - return $array[$entryId]; - } - - return '0'; - } - - /** * @param Collection $accounts * @param Carbon $start @@ -179,4 +163,19 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator return array_unique($ids); } + + /** + * @param $array + * @param $entryId + * + * @return string + */ + protected function isInArray($array, $entryId) + { + if (isset($array[$entryId])) { + return $array[$entryId]; + } + + return '0'; + } } diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index fa25cd857b..65db28db6b 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -106,6 +106,40 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator return $data; } + /** + * @param Collection $entries + * + * @return array + */ + public function multiYear(Collection $entries) + { + // dataset: + $data = [ + 'count' => 0, + 'labels' => [], + 'datasets' => [], + ]; + // get labels from one of the budgets (assuming there's at least one): + $first = $entries->first(); + $keys = array_keys($first['budgeted']); + foreach ($keys as $year) { + $data['labels'][] = strval($year); + } + + // then, loop all entries and create datasets: + foreach ($entries as $entry) { + $name = $entry['name']; + $spent = $entry['spent']; + $budgeted = $entry['budgeted']; + $data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)]; + $data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)]; + } + $data['count'] = count($data['datasets']); + + return $data; + + } + /** * @param Collection $budgets * @param Collection $entries @@ -140,38 +174,4 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator return $data; } - - /** - * @param Collection $entries - * - * @return array - */ - public function multiYear(Collection $entries) - { - // dataset: - $data = [ - 'count' => 0, - 'labels' => [], - 'datasets' => [], - ]; - // get labels from one of the budgets (assuming there's at least one): - $first = $entries->first(); - $keys = array_keys($first['budgeted']); - foreach ($keys as $year) { - $data['labels'][] = strval($year); - } - - // then, loop all entries and create datasets: - foreach ($entries as $entry) { - $name = $entry['name']; - $spent = $entry['spent']; - $budgeted = $entry['budgeted']; - $data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)]; - $data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)]; - } - $data['count'] = count($data['datasets']); - - return $data; - - } } diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 981a1a0a8a..692287d0f7 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -19,14 +19,14 @@ use Symfony\Component\HttpFoundation\File\UploadedFile; class AttachmentHelper implements AttachmentHelperInterface { - /** @var int */ - protected $maxUploadSize; - /** @var array */ - protected $allowedMimes; /** @var MessageBag */ public $errors; /** @var MessageBag */ public $messages; + /** @var array */ + protected $allowedMimes; + /** @var int */ + protected $maxUploadSize; /** * @@ -51,6 +51,22 @@ class AttachmentHelper implements AttachmentHelperInterface return $path; } + /** + * @return MessageBag + */ + public function getErrors() + { + return $this->errors; + } + + /** + * @return MessageBag + */ + public function getMessages() + { + return $this->messages; + } + /** * @param Model $model * @@ -98,27 +114,6 @@ class AttachmentHelper implements AttachmentHelperInterface return false; } - /** - * @param UploadedFile $file - * @param Model $model - * - * @return bool - */ - protected function validateUpload(UploadedFile $file, Model $model) - { - if (!$this->validMime($file)) { - return false; - } - if (!$this->validSize($file)) { - return false; - } - if ($this->hasFile($file, $model)) { - return false; - } - - return true; - } - /** * @param UploadedFile $file * @param Model $model @@ -205,19 +200,24 @@ class AttachmentHelper implements AttachmentHelperInterface } /** - * @return MessageBag + * @param UploadedFile $file + * @param Model $model + * + * @return bool */ - public function getErrors() + protected function validateUpload(UploadedFile $file, Model $model) { - return $this->errors; - } + if (!$this->validMime($file)) { + return false; + } + if (!$this->validSize($file)) { + return false; + } + if ($this->hasFile($file, $model)) { + return false; + } - /** - * @return MessageBag - */ - public function getMessages() - { - return $this->messages; + return true; } diff --git a/app/Helpers/Attachments/AttachmentHelperInterface.php b/app/Helpers/Attachments/AttachmentHelperInterface.php index f25971a173..bcc36a642f 100644 --- a/app/Helpers/Attachments/AttachmentHelperInterface.php +++ b/app/Helpers/Attachments/AttachmentHelperInterface.php @@ -15,11 +15,11 @@ interface AttachmentHelperInterface { /** - * @param Model $model + * @param Attachment $attachment * - * @return bool + * @return mixed */ - public function saveAttachmentsForModel(Model $model); + public function getAttachmentLocation(Attachment $attachment); /** * @return MessageBag @@ -32,10 +32,10 @@ interface AttachmentHelperInterface public function getMessages(); /** - * @param Attachment $attachment + * @param Model $model * - * @return mixed + * @return bool */ - public function getAttachmentLocation(Attachment $attachment); + public function saveAttachmentsForModel(Model $model); } diff --git a/app/Helpers/Csv/Data.php b/app/Helpers/Csv/Data.php index 2d6388792b..72593ce30f 100644 --- a/app/Helpers/Csv/Data.php +++ b/app/Helpers/Csv/Data.php @@ -15,36 +15,26 @@ class Data /** @var string */ protected $csvFileContent; - - /** @var string */ - protected $delimiter; - /** @var string */ protected $csvFileLocation; - /** @var string */ protected $dateFormat; - + /** @var string */ + protected $delimiter; /** @var bool */ protected $hasHeaders; - - /** @var array */ - protected $map = []; - - /** @var array */ - protected $mapped = []; - - /** @var Reader */ - protected $reader; - - /** @var array */ - protected $roles = []; - - /** @var array */ - protected $specifix = []; - /** @var int */ protected $importAccount = 0; + /** @var array */ + protected $map = []; + /** @var array */ + protected $mapped = []; + /** @var Reader */ + protected $reader; + /** @var array */ + protected $roles = []; + /** @var array */ + protected $specifix = []; /** */ @@ -61,67 +51,41 @@ class Data $this->sessionDelimiter(); } - protected function sessionHasHeaders() + /** + * + * @return string + */ + public function getCsvFileContent() { - if (Session::has('csv-has-headers')) { - $this->hasHeaders = (bool)Session::get('csv-has-headers'); - } + return $this->csvFileContent; } - protected function sessionImportAccount() + /** + * + * @param string $csvFileContent + */ + public function setCsvFileContent($csvFileContent) { - if (Session::has('csv-import-account')) { - $this->importAccount = intval(Session::get('csv-import-account')); - } + $this->csvFileContent = $csvFileContent; } - protected function sessionDateFormat() + /** + * + * @return string + */ + public function getCsvFileLocation() { - if (Session::has('csv-date-format')) { - $this->dateFormat = (string)Session::get('csv-date-format'); - } + return $this->csvFileLocation; } - protected function sessionCsvFileLocation() + /** + * + * @param string $csvFileLocation + */ + public function setCsvFileLocation($csvFileLocation) { - if (Session::has('csv-file')) { - $this->csvFileLocation = (string)Session::get('csv-file'); - } - } - - protected function sessionMap() - { - if (Session::has('csv-map')) { - $this->map = (array)Session::get('csv-map'); - } - } - - protected function sessionRoles() - { - if (Session::has('csv-roles')) { - $this->roles = (array)Session::get('csv-roles'); - } - } - - protected function sessionMapped() - { - if (Session::has('csv-mapped')) { - $this->mapped = (array)Session::get('csv-mapped'); - } - } - - protected function sessionSpecifix() - { - if (Session::has('csv-specifix')) { - $this->specifix = (array)Session::get('csv-specifix'); - } - } - - protected function sessionDelimiter() - { - if (Session::has('csv-delimiter')) { - $this->delimiter = Session::get('csv-delimiter'); - } + Session::put('csv-file', $csvFileLocation); + $this->csvFileLocation = $csvFileLocation; } /** @@ -145,31 +109,21 @@ class Data /** * - * @param int $importAccount + * @return string */ - public function setImportAccount($importAccount) + public function getDelimiter() { - Session::put('csv-import-account', $importAccount); - $this->importAccount = $importAccount; + return $this->delimiter; } /** * - * @return bool + * @param string $delimiter */ - public function hasHeaders() + public function setDelimiter($delimiter) { - return $this->hasHeaders; - } - - /** - * - * @param bool $hasHeaders - */ - public function setHasHeaders($hasHeaders) - { - Session::put('csv-has-headers', $hasHeaders); - $this->hasHeaders = $hasHeaders; + Session::put('csv-delimiter', $delimiter); + $this->delimiter = $delimiter; } /** @@ -228,51 +182,6 @@ class Data return $this->reader; } - protected function loadCsvFile() - { - $file = $this->getCsvFileLocation(); - $content = file_get_contents($file); - $contentDecrypted = Crypt::decrypt($content); - $this->setCsvFileContent($contentDecrypted); - } - - /** - * - * @return string - */ - public function getCsvFileLocation() - { - return $this->csvFileLocation; - } - - /** - * - * @param string $csvFileLocation - */ - public function setCsvFileLocation($csvFileLocation) - { - Session::put('csv-file', $csvFileLocation); - $this->csvFileLocation = $csvFileLocation; - } - - /** - * - * @return string - */ - public function getCsvFileContent() - { - return $this->csvFileContent; - } - - /** - * - * @param string $csvFileContent - */ - public function setCsvFileContent($csvFileContent) - { - $this->csvFileContent = $csvFileContent; - } - /** * * @return array @@ -313,20 +222,101 @@ class Data /** * - * @return string + * @return bool */ - public function getDelimiter() + public function hasHeaders() { - return $this->delimiter; + return $this->hasHeaders; } /** * - * @param string $delimiter + * @param bool $hasHeaders */ - public function setDelimiter($delimiter) + public function setHasHeaders($hasHeaders) { - Session::put('csv-delimiter', $delimiter); - $this->delimiter = $delimiter; + Session::put('csv-has-headers', $hasHeaders); + $this->hasHeaders = $hasHeaders; + } + + /** + * + * @param int $importAccount + */ + public function setImportAccount($importAccount) + { + Session::put('csv-import-account', $importAccount); + $this->importAccount = $importAccount; + } + + protected function loadCsvFile() + { + $file = $this->getCsvFileLocation(); + $content = file_get_contents($file); + $contentDecrypted = Crypt::decrypt($content); + $this->setCsvFileContent($contentDecrypted); + } + + protected function sessionCsvFileLocation() + { + if (Session::has('csv-file')) { + $this->csvFileLocation = (string)Session::get('csv-file'); + } + } + + protected function sessionDateFormat() + { + if (Session::has('csv-date-format')) { + $this->dateFormat = (string)Session::get('csv-date-format'); + } + } + + protected function sessionDelimiter() + { + if (Session::has('csv-delimiter')) { + $this->delimiter = Session::get('csv-delimiter'); + } + } + + protected function sessionHasHeaders() + { + if (Session::has('csv-has-headers')) { + $this->hasHeaders = (bool)Session::get('csv-has-headers'); + } + } + + protected function sessionImportAccount() + { + if (Session::has('csv-import-account')) { + $this->importAccount = intval(Session::get('csv-import-account')); + } + } + + protected function sessionMap() + { + if (Session::has('csv-map')) { + $this->map = (array)Session::get('csv-map'); + } + } + + protected function sessionMapped() + { + if (Session::has('csv-mapped')) { + $this->mapped = (array)Session::get('csv-mapped'); + } + } + + protected function sessionRoles() + { + if (Session::has('csv-roles')) { + $this->roles = (array)Session::get('csv-roles'); + } + } + + protected function sessionSpecifix() + { + if (Session::has('csv-specifix')) { + $this->specifix = (array)Session::get('csv-specifix'); + } } } diff --git a/app/Helpers/Csv/Importer.php b/app/Helpers/Csv/Importer.php index 936cee2956..2aa4274b26 100644 --- a/app/Helpers/Csv/Importer.php +++ b/app/Helpers/Csv/Importer.php @@ -34,6 +34,8 @@ class Importer protected $importRow; /** @var int */ protected $imported = 0; + /** @var Collection */ + protected $journals; /** @var array */ protected $map; /** @var array */ @@ -45,9 +47,6 @@ class Importer /** @var array */ protected $specifix = []; - /** @var Collection */ - protected $journals; - /** * Used by CsvController. * @@ -68,6 +67,14 @@ class Importer return $this->imported; } + /** + * @return Collection + */ + public function getJournals() + { + return $this->journals; + } + /** * Used by CsvController * @@ -79,14 +86,13 @@ class Importer } /** - * @return Collection + * @return array */ - public function getJournals() + public function getSpecifix() { - return $this->journals; + return is_array($this->specifix) ? $this->specifix : []; } - /** * @throws FireflyException */ @@ -118,136 +124,11 @@ class Importer } /** - * @param int $index - * - * @return bool + * @param Data $data */ - protected function parseRow($index) + public function setData($data) { - return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders()); - } - - /** - * @param $row - * - * @throws FireflyException - * @return string|bool - */ - protected function importRow($row) - { - - $data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional - foreach ($row as $index => $value) { - $role = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore'; - $class = Config::get('csv.roles.' . $role . '.converter'); - $field = Config::get('csv.roles.' . $role . '.field'); - - Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':'); - - /** @var ConverterInterface $converter */ - $converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class); - $converter->setData($data); // the complete array so far. - $converter->setField($field); - $converter->setIndex($index); - $converter->setMapped($this->mapped); - $converter->setValue($value); - $converter->setRole($role); - $data[$field] = $converter->convert(); - } - // move to class vars. - $this->importData = $data; - $this->importRow = $row; - unset($data, $row); - // post processing and validating. - $this->postProcess(); - $result = $this->validateData(); - - if (!($result === true)) { - return $result; // return error. - } - $journal = $this->createTransactionJournal(); - - return $journal; - } - - /** - * @return array - */ - protected function getFiller() - { - $filler = []; - foreach (Config::get('csv.roles') as $role) { - if (isset($role['field'])) { - $fieldName = $role['field']; - $filler[$fieldName] = null; - } - } - // some extra's: - $filler['bill-id'] = null; - $filler['opposing-account-object'] = null; - $filler['asset-account-object'] = null; - $filler['amount-modifier'] = '1'; - - return $filler; - - } - - /** - * Row denotes the original data. - * - * @return void - */ - protected function postProcess() - { - // do bank specific fixes (must be enabled but now all of them. - - foreach ($this->getSpecifix() as $className) { - /** @var SpecifixInterface $specifix */ - $specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className); - $specifix->setData($this->importData); - $specifix->setRow($this->importRow); - Log::debug('Now post-process specifix named ' . $className . ':'); - $this->importData = $specifix->fix(); - } - - - $set = Config::get('csv.post_processors'); - foreach ($set as $className) { - /** @var PostProcessorInterface $postProcessor */ - $postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className); - $postProcessor->setData($this->importData); - Log::debug('Now post-process processor named ' . $className . ':'); - $this->importData = $postProcessor->process(); - } - - } - - /** - * @return array - */ - public function getSpecifix() - { - return is_array($this->specifix) ? $this->specifix : []; - } - - /** - * - * @return bool|string - */ - protected function validateData() - { - if (is_null($this->importData['date']) && is_null($this->importData['date-rent'])) { - return 'No date value for this row.'; - } - if (is_null($this->importData['opposing-account-object'])) { - return 'Opposing account is null'; - } - - if (!($this->importData['asset-account-object'] instanceof Account)) { - return 'No asset account to import into.'; - } - - return true; + $this->data = $data; } /** @@ -309,6 +190,28 @@ class Importer return $journal; } + /** + * @return array + */ + protected function getFiller() + { + $filler = []; + foreach (Config::get('csv.roles') as $role) { + if (isset($role['field'])) { + $fieldName = $role['field']; + $filler[$fieldName] = null; + } + } + // some extra's: + $filler['bill-id'] = null; + $filler['opposing-account-object'] = null; + $filler['asset-account-object'] = null; + $filler['amount-modifier'] = '1'; + + return $filler; + + } + /** * @return TransactionType */ @@ -326,6 +229,89 @@ class Importer return $transactionType; } + /** + * @param $row + * + * @throws FireflyException + * @return string|bool + */ + protected function importRow($row) + { + + $data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional + foreach ($row as $index => $value) { + $role = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore'; + $class = Config::get('csv.roles.' . $role . '.converter'); + $field = Config::get('csv.roles.' . $role . '.field'); + + Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':'); + + /** @var ConverterInterface $converter */ + $converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class); + $converter->setData($data); // the complete array so far. + $converter->setField($field); + $converter->setIndex($index); + $converter->setMapped($this->mapped); + $converter->setValue($value); + $converter->setRole($role); + $data[$field] = $converter->convert(); + } + // move to class vars. + $this->importData = $data; + $this->importRow = $row; + unset($data, $row); + // post processing and validating. + $this->postProcess(); + $result = $this->validateData(); + + if (!($result === true)) { + return $result; // return error. + } + $journal = $this->createTransactionJournal(); + + return $journal; + } + + /** + * @param int $index + * + * @return bool + */ + protected function parseRow($index) + { + return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders()); + } + + /** + * Row denotes the original data. + * + * @return void + */ + protected function postProcess() + { + // do bank specific fixes (must be enabled but now all of them. + + foreach ($this->getSpecifix() as $className) { + /** @var SpecifixInterface $specifix */ + $specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className); + $specifix->setData($this->importData); + $specifix->setRow($this->importRow); + Log::debug('Now post-process specifix named ' . $className . ':'); + $this->importData = $specifix->fix(); + } + + + $set = Config::get('csv.post_processors'); + foreach ($set as $className) { + /** @var PostProcessorInterface $postProcessor */ + $postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className); + $postProcessor->setData($this->importData); + Log::debug('Now post-process processor named ' . $className . ':'); + $this->importData = $postProcessor->process(); + } + + } + /** * @param TransactionJournal $journal */ @@ -361,11 +347,23 @@ class Importer } /** - * @param Data $data + * + * @return bool|string */ - public function setData($data) + protected function validateData() { - $this->data = $data; + if (is_null($this->importData['date']) && is_null($this->importData['date-rent'])) { + return 'No date value for this row.'; + } + if (is_null($this->importData['opposing-account-object'])) { + return 'Opposing account is null'; + } + + if (!($this->importData['asset-account-object'] instanceof Account)) { + return 'No asset account to import into.'; + } + + return true; } } diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index ecdc75b8bb..b49881e81c 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -54,8 +54,50 @@ class AbnAmroDescription $this->row = $row; } + /** + * Parses the current description with costs from ABN AMRO itself + * + * @return boolean true if the description is GEA/BEA-format, false otherwise + */ + protected function parseABNAMRODescription() + { + // See if the current description is formatted in ABN AMRO format + if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) { + Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.'); + + $this->data['opposing-account-name'] = "ABN AMRO"; + $this->data['description'] = $matches[1]; + + return true; + } + + return false; + } + + /** + * Parses the current description in GEA/BEA format + * + * @return boolean true if the description is GEA/BEAformat, false otherwise + */ + protected function parseGEABEADescription() + { + // See if the current description is formatted in GEA/BEA format + if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) { + Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.'); + + // description and opposing account will be the same. + $this->data['opposing-account-name'] = $matches[4]; + $this->data['description'] = $matches[4]; + + return true; + } + + return false; + } + /** * Parses the current description in SEPA format + * * @return boolean true if the description is SEPA format, false otherwise */ protected function parseSepaDescription() @@ -94,6 +136,7 @@ class AbnAmroDescription /** * Parses the current description in TRTP format + * * @return boolean true if the description is TRTP format, false otherwise */ protected function parseTRTPDescription() @@ -108,9 +151,9 @@ class AbnAmroDescription switch (strtoupper($key)) { // is not being used. -// case 'TRTP': -// $type = $value; -// break; + // case 'TRTP': + // $type = $value; + // break; case 'NAME': $this->data['opposing-account-name'] = $value; break; @@ -131,43 +174,4 @@ class AbnAmroDescription return false; } - /** - * Parses the current description in GEA/BEA format - * @return boolean true if the description is GEA/BEAformat, false otherwise - */ - protected function parseGEABEADescription() - { - // See if the current description is formatted in GEA/BEA format - if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) { - Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.'); - - // description and opposing account will be the same. - $this->data['opposing-account-name'] = $matches[4]; - $this->data['description'] = $matches[4]; - - return true; - } - - return false; - } - - /** - * Parses the current description with costs from ABN AMRO itself - * @return boolean true if the description is GEA/BEA-format, false otherwise - */ - protected function parseABNAMRODescription() - { - // See if the current description is formatted in ABN AMRO format - if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) { - Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.'); - - $this->data['opposing-account-name'] = "ABN AMRO"; - $this->data['description'] = $matches[1]; - - return true; - } - - return false; - } - } diff --git a/app/Helpers/Csv/Wizard.php b/app/Helpers/Csv/Wizard.php index 03ed929ce0..bf93c16ec8 100644 --- a/app/Helpers/Csv/Wizard.php +++ b/app/Helpers/Csv/Wizard.php @@ -168,17 +168,6 @@ class Wizard implements WizardInterface } - /** - * @param bool $hasHeaders - * @param int $index - * - * @return bool - */ - protected function useRow($hasHeaders, $index) - { - return ($hasHeaders && $index > 1) || !$hasHeaders; - } - /** * @param array $array * @@ -192,4 +181,15 @@ class Wizard implements WizardInterface return $array; } + + /** + * @param bool $hasHeaders + * @param int $index + * + * @return bool + */ + protected function useRow($hasHeaders, $index) + { + return ($hasHeaders && $index > 1) || !$hasHeaders; + } } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 2b13fbe9ae..9cfccfd6f7 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -35,12 +35,10 @@ use Illuminate\Support\Collection; class ReportHelper implements ReportHelperInterface { - /** @var ReportQueryInterface */ - protected $query; - /** @var BudgetRepositoryInterface */ protected $budgetRepository; - + /** @var ReportQueryInterface */ + protected $query; /** @var TagRepositoryInterface */ protected $tagRepository; @@ -60,68 +58,6 @@ class ReportHelper implements ReportHelperInterface $this->tagRepository = $tagRepository; } - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return CategoryCollection - */ - public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts) - { - $object = new CategoryCollection; - - /** - * GET CATEGORIES: - */ - /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); - - $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); - foreach ($set as $category) { - $object->addCategory($category); - } - - return $object; - } - - /** - * @param Carbon $date - * - * @return array - */ - public function listOfMonths(Carbon $date) - { - - $start = clone $date; - $end = Carbon::now(); - $months = []; - while ($start <= $end) { - $year = $start->year; - - if (!isset($months[$year])) { - $months[$year] = [ - 'start' => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'), - 'end' => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'), - 'months' => [], - ]; - } - - $currentEnd = clone $start; - $currentEnd->endOfMonth(); - $months[$year]['months'][] = [ - 'formatted' => $start->formatLocalized('%B %Y'), - 'start' => $start->format('Y-m-d'), - 'end' => $currentEnd->format('Y-m-d'), - 'month' => $start->month, - 'year' => $year, - ]; - $start->addMonth(); - } - - return $months; - } - /** * This method generates a full report for the given period on all * given accounts @@ -211,47 +147,86 @@ class ReportHelper implements ReportHelperInterface } /** - * Get a full report on the users incomes during the period for the given accounts. - * * @param Carbon $start * @param Carbon $end * @param Collection $accounts * - * @return Income + * @return Balance */ - public function getIncomeReport($start, $end, Collection $accounts) + public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) { - $object = new Income; - $set = $this->query->income($accounts, $start, $end); + $balance = new Balance; - foreach ($set as $entry) { - $object->addToTotal($entry->journalAmount); - $object->addOrCreateIncome($entry); + // build a balance header: + $header = new BalanceHeader; + $budgets = $this->budgetRepository->getBudgetsAndLimitsInRange($start, $end); + $spentData = $this->budgetRepository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end); + foreach ($accounts as $account) { + $header->addAccount($account); } - return $object; + /** @var BudgetModel $budget */ + foreach ($budgets as $budget) { + $balance->addBalanceLine($this->createBalanceLine($budget, $accounts, $spentData)); + } + + $balance->addBalanceLine($this->createEmptyBalanceLine($accounts, $spentData)); + $balance->addBalanceLine($this->createTagsBalanceLine($accounts, $start, $end)); + $balance->addBalanceLine($this->createDifferenceBalanceLine($accounts, $spentData, $start, $end)); + + $balance->setBalanceHeader($header); + + return $balance; } /** - * Get a full report on the users expenses during the period for a list of accounts. + * This method generates a full report for the given period on all + * the users bills and their payments. + * + * Excludes bills which have not had a payment on the mentioned accounts. * * @param Carbon $start * @param Carbon $end * @param Collection $accounts * - * @return Expense + * @return BillCollection */ - public function getExpenseReport($start, $end, Collection $accounts) + public function getBillReport(Carbon $start, Carbon $end, Collection $accounts) { - $object = new Expense; - $set = $this->query->expense($accounts, $start, $end); + /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); + $bills = $repository->getBillsForAccounts($accounts); + $journals = $repository->getAllJournalsInRange($bills, $start, $end); + $collection = new BillCollection; + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $billLine = new BillLine; + $billLine->setBill($bill); + $billLine->setActive(intval($bill->active) == 1); + $billLine->setMin($bill->amount_min); + $billLine->setMax($bill->amount_max); + + // is hit in period? + bcscale(2); + + $entry = $journals->filter( + function (TransactionJournal $journal) use ($bill) { + return $journal->bill_id == $bill->id; + } + ); + if (!is_null($entry->first())) { + $billLine->setAmount($entry->first()->journalAmount); + $billLine->setHit(true); + } else { + $billLine->setHit(false); + } + + $collection->addBill($billLine); - foreach ($set as $entry) { - $object->addToTotal($entry->journalAmount); // can be positive, if it's a transfer - $object->addOrCreateExpense($entry); } - return $object; + return $collection; } /** @@ -338,82 +313,105 @@ class ReportHelper implements ReportHelperInterface * @param Carbon $end * @param Collection $accounts * - * @return Balance + * @return CategoryCollection */ - public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) + public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts) { - $balance = new Balance; + $object = new CategoryCollection; - // build a balance header: - $header = new BalanceHeader; - $budgets = $this->budgetRepository->getBudgetsAndLimitsInRange($start, $end); - $spentData = $this->budgetRepository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end); - foreach ($accounts as $account) { - $header->addAccount($account); + /** + * GET CATEGORIES: + */ + /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); + + $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); + foreach ($set as $category) { + $object->addCategory($category); } - /** @var BudgetModel $budget */ - foreach ($budgets as $budget) { - $balance->addBalanceLine($this->createBalanceLine($budget, $accounts, $spentData)); - } - - $balance->addBalanceLine($this->createEmptyBalanceLine($accounts, $spentData)); - $balance->addBalanceLine($this->createTagsBalanceLine($accounts, $start, $end)); - $balance->addBalanceLine($this->createDifferenceBalanceLine($accounts, $spentData, $start, $end)); - - $balance->setBalanceHeader($header); - - return $balance; + return $object; } /** - * This method generates a full report for the given period on all - * the users bills and their payments. - * - * Excludes bills which have not had a payment on the mentioned accounts. + * Get a full report on the users expenses during the period for a list of accounts. * * @param Carbon $start * @param Carbon $end * @param Collection $accounts * - * @return BillCollection + * @return Expense */ - public function getBillReport(Carbon $start, Carbon $end, Collection $accounts) + public function getExpenseReport($start, $end, Collection $accounts) { - /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); - $bills = $repository->getBillsForAccounts($accounts); - $journals = $repository->getAllJournalsInRange($bills, $start, $end); - $collection = new BillCollection; - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $billLine = new BillLine; - $billLine->setBill($bill); - $billLine->setActive(intval($bill->active) == 1); - $billLine->setMin($bill->amount_min); - $billLine->setMax($bill->amount_max); - - // is hit in period? - bcscale(2); - - $entry = $journals->filter( - function (TransactionJournal $journal) use ($bill) { - return $journal->bill_id == $bill->id; - } - ); - if (!is_null($entry->first())) { - $billLine->setAmount($entry->first()->journalAmount); - $billLine->setHit(true); - } else { - $billLine->setHit(false); - } - - $collection->addBill($billLine); + $object = new Expense; + $set = $this->query->expense($accounts, $start, $end); + foreach ($set as $entry) { + $object->addToTotal($entry->journalAmount); // can be positive, if it's a transfer + $object->addOrCreateExpense($entry); } - return $collection; + return $object; + } + + /** + * Get a full report on the users incomes during the period for the given accounts. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return Income + */ + public function getIncomeReport($start, $end, Collection $accounts) + { + $object = new Income; + $set = $this->query->income($accounts, $start, $end); + + foreach ($set as $entry) { + $object->addToTotal($entry->journalAmount); + $object->addOrCreateIncome($entry); + } + + return $object; + } + + /** + * @param Carbon $date + * + * @return array + */ + public function listOfMonths(Carbon $date) + { + + $start = clone $date; + $end = Carbon::now(); + $months = []; + while ($start <= $end) { + $year = $start->year; + + if (!isset($months[$year])) { + $months[$year] = [ + 'start' => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'), + 'end' => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'), + 'months' => [], + ]; + } + + $currentEnd = clone $start; + $currentEnd->endOfMonth(); + $months[$year]['months'][] = [ + 'formatted' => $start->formatLocalized('%B %Y'), + 'start' => $start->format('Y-m-d'), + 'end' => $currentEnd->format('Y-m-d'), + 'month' => $start->month, + 'year' => $year, + ]; + $start->addMonth(); + } + + return $months; } /** @@ -478,6 +476,56 @@ class ReportHelper implements ReportHelperInterface return $line; } + /** + * @param Collection $accounts + * @param Collection $spentData + * @param Carbon $start + * @param Carbon $end + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * + * @return BalanceLine + */ + private function createDifferenceBalanceLine(Collection $accounts, Collection $spentData, Carbon $start, Carbon $end) + { + $diff = new BalanceLine; + $tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end); + + $diff->setRole(BalanceLine::ROLE_DIFFROLE); + + foreach ($accounts as $account) { + $entry = $spentData->filter( + function (TransactionJournal $model) use ($account) { + return $model->account_id == $account->id && is_null($model->budget_id); + } + ); + $spent = 0; + if (!is_null($entry->first())) { + $spent = $entry->first()->spent; + } + $leftEntry = $tagsLeft->filter( + function (Tag $tag) use ($account) { + return $tag->account_id == $account->id; + } + ); + $left = 0; + if (!is_null($leftEntry->first())) { + $left = $leftEntry->first()->sum; + } + bcscale(2); + $diffValue = bcadd($spent, $left); + + // difference: + $diffEntry = new BalanceEntry; + $diffEntry->setAccount($account); + $diffEntry->setSpent($diffValue); + $diff->addBalanceEntry($diffEntry); + + } + + return $diff; + } + /** * @param Collection $accounts * @param Collection $spentData @@ -546,54 +594,4 @@ class ReportHelper implements ReportHelperInterface return $tags; } - - /** - * @param Collection $accounts - * @param Collection $spentData - * @param Carbon $start - * @param Carbon $end - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * - * @return BalanceLine - */ - private function createDifferenceBalanceLine(Collection $accounts, Collection $spentData, Carbon $start, Carbon $end) - { - $diff = new BalanceLine; - $tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end); - - $diff->setRole(BalanceLine::ROLE_DIFFROLE); - - foreach ($accounts as $account) { - $entry = $spentData->filter( - function (TransactionJournal $model) use ($account) { - return $model->account_id == $account->id && is_null($model->budget_id); - } - ); - $spent = 0; - if (!is_null($entry->first())) { - $spent = $entry->first()->spent; - } - $leftEntry = $tagsLeft->filter( - function (Tag $tag) use ($account) { - return $tag->account_id == $account->id; - } - ); - $left = 0; - if (!is_null($leftEntry->first())) { - $left = $leftEntry->first()->sum; - } - bcscale(2); - $diffValue = bcadd($spent, $left); - - // difference: - $diffEntry = new BalanceEntry; - $diffEntry->setAccount($account); - $diffEntry->setSpent($diffValue); - $diff->addBalanceEntry($diffEntry); - - } - - return $diff; - } } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index f52036cac2..9759d755d7 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -32,6 +32,15 @@ interface ReportHelperInterface */ public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts); + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return Balance + */ + public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts); + /** * This method generates a full report for the given period on all * the users bills and their payments. @@ -46,15 +55,6 @@ interface ReportHelperInterface */ public function getBillReport(Carbon $start, Carbon $end, Collection $accounts); - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return Balance - */ - public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts); - /** * @param Carbon $start * @param Carbon $end diff --git a/app/Helpers/Report/ReportQuery.php b/app/Helpers/Report/ReportQuery.php index 0180571484..24027b2e0c 100644 --- a/app/Helpers/Report/ReportQuery.php +++ b/app/Helpers/Report/ReportQuery.php @@ -17,51 +17,6 @@ use Illuminate\Support\Collection; class ReportQuery implements ReportQueryInterface { - /** - * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) - * grouped by month like so: "2015-01" => '123.45' - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end) - { - $ids = $accounts->pluck('id')->toArray(); - $query = Auth::user()->transactionjournals() - ->leftJoin( - 'transactions AS t_from', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); - } - ) - ->leftJoin( - 'transactions AS t_to', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); - } - ) - ->whereIn('t_from.account_id', $ids) - ->whereNotIn('t_to.account_id', $ids) - ->after($start) - ->before($end) - ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) - ->groupBy('dateFormatted') - ->get( - [ - DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), - DB::Raw('SUM(`t_from`.`amount`) AS `sum`'), - ] - ); - $array = []; - foreach ($query as $result) { - $array[$result->dateFormatted] = $result->sum; - } - - return $array; - - } - /** * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) * grouped by month like so: "2015-01" => '123.45' @@ -106,6 +61,41 @@ class ReportQuery implements ReportQueryInterface return $array; } + /** + * This method returns all the "out" transaction journals for the given account and given period. The amount + * is stored in "journalAmount". + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function expense(Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + $set = Auth::user()->transactionjournals() + ->leftJoin( + 'transactions as t_from', function (JoinClause $join) { + $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions as t_to', function (JoinClause $join) { + $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); + } + ) + ->leftJoin('accounts', 't_to.account_id', '=', 'accounts.id') + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) + ->before($end) + ->after($start) + ->whereIn('t_from.account_id', $ids) + ->whereNotIn('t_to.account_id', $ids) + ->get(['transaction_journals.*', 't_from.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']); + + return $set; + } + /** * This method returns all the "in" transaction journals for the given account and given period. The amount * is stored in "journalAmount". @@ -142,37 +132,47 @@ class ReportQuery implements ReportQueryInterface } /** - * This method returns all the "out" transaction journals for the given account and given period. The amount - * is stored in "journalAmount". + * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) + * grouped by month like so: "2015-01" => '123.45' * * @param Collection $accounts * @param Carbon $start * @param Carbon $end * - * @return Collection + * @return array */ - public function expense(Collection $accounts, Carbon $start, Carbon $end) + public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end) { - $ids = $accounts->pluck('id')->toArray(); - $set = Auth::user()->transactionjournals() - ->leftJoin( - 'transactions as t_from', function (JoinClause $join) { - $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); - } - ) - ->leftJoin( - 'transactions as t_to', function (JoinClause $join) { - $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); - } - ) - ->leftJoin('accounts', 't_to.account_id', '=', 'accounts.id') - ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) - ->before($end) - ->after($start) - ->whereIn('t_from.account_id', $ids) - ->whereNotIn('t_to.account_id', $ids) - ->get(['transaction_journals.*', 't_from.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']); + $ids = $accounts->pluck('id')->toArray(); + $query = Auth::user()->transactionjournals() + ->leftJoin( + 'transactions AS t_from', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS t_to', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); + } + ) + ->whereIn('t_from.account_id', $ids) + ->whereNotIn('t_to.account_id', $ids) + ->after($start) + ->before($end) + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) + ->groupBy('dateFormatted') + ->get( + [ + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), + DB::Raw('SUM(`t_from`.`amount`) AS `sum`'), + ] + ); + $array = []; + foreach ($query as $result) { + $array[$result->dateFormatted] = $result->sum; + } + + return $array; - return $set; } } diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 6a9ebf260b..95f7dc6ec3 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -61,6 +61,18 @@ class AccountRepository implements AccountRepositoryInterface return true; } + /** + * @deprecated + * + * @param $accountId + * + * @return Account + */ + public function find($accountId) + { + return Auth::user()->accounts()->findOrNew($accountId); + } + /** * @param array $types * @@ -84,7 +96,6 @@ class AccountRepository implements AccountRepositoryInterface return $result; } - /** * This method returns the users credit cards, along with some basic information about the * balance they have on their CC. To be used in the JSON boxes on the front page that say @@ -465,29 +476,6 @@ class AccountRepository implements AccountRepositoryInterface return $newAccount; } - /** - * @param Account $account - * @param array $data - */ - protected function storeMetadata(Account $account, array $data) - { - $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType']; - foreach ($validFields as $field) { - if (isset($data[$field])) { - $metaData = new AccountMeta( - [ - 'account_id' => $account->id, - 'name' => $field, - 'data' => $data[$field], - ] - ); - $metaData->save(); - } - - - } - } - /** * @param Account $account * @param Account $opposing @@ -536,33 +524,24 @@ class AccountRepository implements AccountRepositoryInterface /** * @param Account $account * @param array $data - * */ - protected function updateMetadata(Account $account, array $data) + protected function storeMetadata(Account $account, array $data) { $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType']; - foreach ($validFields as $field) { - $entry = $account->accountMeta()->where('name', $field)->first(); - if (isset($data[$field])) { - // update if new data is present: - if (!is_null($entry)) { - $entry->data = $data[$field]; - $entry->save(); - } else { - $metaData = new AccountMeta( - [ - 'account_id' => $account->id, - 'name' => $field, - 'data' => $data[$field], - ] - ); - $metaData->save(); - } + $metaData = new AccountMeta( + [ + 'account_id' => $account->id, + 'name' => $field, + 'data' => $data[$field], + ] + ); + $metaData->save(); } - } + + } } /** @@ -592,14 +571,34 @@ class AccountRepository implements AccountRepositoryInterface } /** - * @deprecated + * @param Account $account + * @param array $data * - * @param $accountId - * - * @return Account */ - public function find($accountId) + protected function updateMetadata(Account $account, array $data) { - return Auth::user()->accounts()->findOrNew($accountId); + $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType']; + + foreach ($validFields as $field) { + $entry = $account->accountMeta()->where('name', $field)->first(); + + if (isset($data[$field])) { + // update if new data is present: + if (!is_null($entry)) { + $entry->data = $data[$field]; + $entry->save(); + } else { + $metaData = new AccountMeta( + [ + 'account_id' => $account->id, + 'name' => $field, + 'data' => $data[$field], + ] + ); + $metaData->save(); + } + } + } + } } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index b18ffb2807..628aca2d15 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -24,6 +24,14 @@ interface AccountRepositoryInterface */ public function countAccounts(array $types); + /** + * @param Account $account + * @param Account $moveTo + * + * @return boolean + */ + public function destroy(Account $account, Account $moveTo = null); + /** * @param $accountId * @@ -33,14 +41,6 @@ interface AccountRepositoryInterface */ public function find($accountId); - /** - * @param Account $account - * @param Account $moveTo - * - * @return boolean - */ - public function destroy(Account $account, Account $moveTo = null); - /** * @param array $types * @@ -48,14 +48,6 @@ interface AccountRepositoryInterface */ public function getAccounts(array $types); - /** - * @param TransactionJournal $journal - * @param Account $account - * - * @return Transaction - */ - public function getFirstTransaction(TransactionJournal $journal, Account $account); - /** * This method returns the users credit cards, along with some basic information about the * balance they have on their CC. To be used in the JSON boxes on the front page that say @@ -70,11 +62,12 @@ interface AccountRepositoryInterface public function getCreditCards(Carbon $date); /** - * Get the accounts of a user that have piggy banks connected to them. + * @param TransactionJournal $journal + * @param Account $account * - * @return Collection + * @return Transaction */ - public function getPiggyBankAccounts(); + public function getFirstTransaction(TransactionJournal $journal, Account $account); /** * @param Preference $preference @@ -101,9 +94,11 @@ interface AccountRepositoryInterface public function getJournals(Account $account, $page); /** - * @return string + * Get the accounts of a user that have piggy banks connected to them. + * + * @return Collection */ - public function sumOfEverything(); + public function getPiggyBankAccounts(); /** * Get savings accounts and the balance difference in the period. @@ -134,6 +129,11 @@ interface AccountRepositoryInterface */ public function store(array $data); + /** + * @return string + */ + public function sumOfEverything(); + /** * @param Account $account * @param array $data diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 4593d1d1fe..b42ee7779e 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -46,6 +46,13 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return true; } + /** + * @return Collection + */ + public function get() + { + return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get(); + } /** * @return int @@ -57,36 +64,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return intval($entry); } - /** - * @return Collection - */ - public function get() - { - return Auth::user()->ruleGroups()->orderBy('order', 'ASC')->get(); - } - - - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ - public function moveUp(RuleGroup $ruleGroup) - { - $order = $ruleGroup->order; - - // find the rule with order-1 and give it order+1 - $other = Auth::user()->ruleGroups()->where('order', ($order - 1))->first(); - if ($other) { - $other->order = ($other->order + 1); - $other->save(); - } - - $ruleGroup->order = ($ruleGroup->order - 1); - $ruleGroup->save(); - $this->resetRuleGroupOrder(); - } - /** * @param RuleGroup $ruleGroup * @@ -108,6 +85,26 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface $this->resetRuleGroupOrder(); } + /** + * @param RuleGroup $ruleGroup + * + * @return bool + */ + public function moveUp(RuleGroup $ruleGroup) + { + $order = $ruleGroup->order; + + // find the rule with order-1 and give it order+1 + $other = Auth::user()->ruleGroups()->where('order', ($order - 1))->first(); + if ($other) { + $other->order = ($other->order + 1); + $other->save(); + } + + $ruleGroup->order = ($ruleGroup->order - 1); + $ruleGroup->save(); + $this->resetRuleGroupOrder(); + } /** * @return bool diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index f1dd3f317b..fbb36df7c4 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -21,6 +21,10 @@ interface RuleGroupRepositoryInterface */ public function destroy(RuleGroup $ruleGroup, RuleGroup $moveTo = null); + /** + * @return Collection + */ + public function get(); /** * @return int @@ -28,9 +32,11 @@ interface RuleGroupRepositoryInterface public function getHighestOrderRuleGroup(); /** - * @return Collection + * @param RuleGroup $ruleGroup + * + * @return bool */ - public function get(); + public function moveDown(RuleGroup $ruleGroup); /** * @param RuleGroup $ruleGroup @@ -39,13 +45,6 @@ interface RuleGroupRepositoryInterface */ public function moveUp(RuleGroup $ruleGroup); - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ - public function moveDown(RuleGroup $ruleGroup); - /** * @return bool */ From 00d059b8df3e3482d252e985f4b7f7ba6a434753 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 14:37:44 +0100 Subject: [PATCH 213/276] More tests. --- .../Controllers/AccountControllerTest.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index e1942c0e70..561e565695 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -28,7 +28,17 @@ class AccountControllerTest extends TestCase public function testDestroy() { - $this->markTestIncomplete(); + $this->be($this->user()); + + $args = [ + '_token' => Session::token(), + ]; + + $this->session(['accounts.delete.url' => 'http://localhost']); + + $response = $this->call('POST', '/accounts/destroy/6', $args); + $this->assertSessionHas('success'); + $this->assertEquals(302, $response->status()); } public function testEdit() @@ -54,6 +64,20 @@ class AccountControllerTest extends TestCase public function testStore() { + $this->be($this->user()); + $this->session(['accounts.create.url' => 'http://localhost']); + $args = [ + '_token' => Session::token(), + 'name' => 'Some kind of test account.', + 'what' => 'asset', + 'amount_currency_id_virtualBalance' => 1, + 'amount_currency_id_openingBalance' => 1, + ]; + + $response = $this->call('POST', '/accounts/store', $args); + $this->assertEquals(302, $response->status()); + $this->assertSessionHas('success'); + $this->markTestIncomplete(); } From c2e04a11bfd5b0f14211c6308fe2d8a481f0aa22 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 16:55:02 +0100 Subject: [PATCH 214/276] Updated run scripts for tests. --- cover.sh | 39 ++++++++++----------------------------- pu.sh | 40 ++++++++++------------------------------ 2 files changed, 20 insertions(+), 59 deletions(-) diff --git a/cover.sh b/cover.sh index 4311932be6..d492b09735 100755 --- a/cover.sh +++ b/cover.sh @@ -6,38 +6,19 @@ cp .env.testing .env # set cover: cp phpunit.cover.xml phpunit.xml -# test! -if [ -z "$1" ] +# delete test databases: +if [ -f storage/database/testing.db ] then - phpdbg -qrr /usr/local/bin/phpunit + rm storage/database/testing.db fi -# directories to look in: -#dirs=("controllers" "database" "factories" "generators" "helpers" "models" "middleware" "repositories" "support") -# -#if [ ! -z "$1" ] -#then -# for i in "${dirs[@]}" -# do -# firstFile="./tests/$i/$1.php" -# secondFile="./tests/$i/$1Test.php" -# if [ -f "$firstFile" ] -# then -# # run it! -# phpunit --verbose $firstFile -# exit $? -# fi -# if [ -f "$secondFile" ] -# then -# # run it! -# phpunit --verbose $secondFile -# exit $? -# fi -# -# -# done -# -#fi +if [ -f storage/database/testing-copy.db ] +then + rm storage/database/testing-copy.db +fi + +# test! +phpdbg -qrr /usr/local/bin/phpunit # restore .env file cp .env.local .env diff --git a/pu.sh b/pu.sh index 2f20bcb843..0029a63e66 100755 --- a/pu.sh +++ b/pu.sh @@ -3,39 +3,19 @@ # set testing environment cp .env.testing .env -# test! -if [ -z "$1" ] +# delete test databases: +if [ -f storage/database/testing.db ] then - #phpunit --verbose - phpunit + rm storage/database/testing.db fi -# directories to look in: -#dirs=("controllers" "database" "factories" "generators" "helpers" "models" "middleware" "repositories" "support") -# -#if [ ! -z "$1" ] -#then -# for i in "${dirs[@]}" -# do -# firstFile="./tests/$i/$1.php" -# secondFile="./tests/$i/$1Test.php" -# if [ -f "$firstFile" ] -# then -# # run it! -# phpunit --verbose $firstFile -# exit $? -# fi -# if [ -f "$secondFile" ] -# then -# # run it! -# phpunit --verbose $secondFile -# exit $? -# fi -# -# -# done -# -#fi +if [ -f storage/database/testing-copy.db ] +then + rm storage/database/testing-copy.db +fi + +# test! +phpunit # restore .env file cp .env.local .env From ddaa53b9404a6393e6c5f2bb0a8e58ccef0e7737 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 16:55:53 +0100 Subject: [PATCH 215/276] Updated test code. --- app/Http/Controllers/AttachmentController.php | 1 - .../Controllers/AccountControllerTest.php | 31 +++++++++++++++++-- .../Controllers/HomeControllerTest.php | 15 ++++++--- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 5a750ac786..14b1580d8f 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -2,7 +2,6 @@ namespace FireflyIII\Http\Controllers; - use Crypt; use File; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index 561e565695..b8973d6d34 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -12,6 +12,9 @@ */ class AccountControllerTest extends TestCase { + /** + * @covers FireflyIII\Http\Controllers\AccountController::create + */ public function testCreate() { $this->be($this->user()); @@ -19,6 +22,9 @@ class AccountControllerTest extends TestCase $this->assertEquals(200, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::delete + */ public function testDelete() { $this->be($this->user()); @@ -26,6 +32,9 @@ class AccountControllerTest extends TestCase $this->assertEquals(200, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::destroy + */ public function testDestroy() { $this->be($this->user()); @@ -41,6 +50,9 @@ class AccountControllerTest extends TestCase $this->assertEquals(302, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::edit + */ public function testEdit() { $this->be($this->user()); @@ -48,6 +60,9 @@ class AccountControllerTest extends TestCase $this->assertEquals(200, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::index + */ public function testIndex() { $this->be($this->user()); @@ -55,6 +70,9 @@ class AccountControllerTest extends TestCase $this->assertEquals(200, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::show + */ public function testShow() { $this->be($this->user()); @@ -62,6 +80,9 @@ class AccountControllerTest extends TestCase $this->assertEquals(200, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::store + */ public function testStore() { $this->be($this->user()); @@ -81,9 +102,15 @@ class AccountControllerTest extends TestCase $this->markTestIncomplete(); } + /** + * @covers FireflyIII\Http\Controllers\AccountController::update + * @todo Implement testUpdate(). + */ public function testUpdate() { - $this->markTestIncomplete(); + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); } - } diff --git a/tests/acceptance/Controllers/HomeControllerTest.php b/tests/acceptance/Controllers/HomeControllerTest.php index 804a9371ad..962e0b9869 100644 --- a/tests/acceptance/Controllers/HomeControllerTest.php +++ b/tests/acceptance/Controllers/HomeControllerTest.php @@ -9,14 +9,17 @@ */ class HomeControllerTest extends TestCase { + /** + * @covers FireflyIII\Http\Controllers\HomeController::dateRange + */ public function testDateRange() { $this->be($this->user()); $args = [ - 'start' => '2012-01-01', - 'end' => '2012-04-01', + 'start' => '2012-01-01', + 'end' => '2012-04-01', '_token' => Session::token(), ]; @@ -26,6 +29,9 @@ class HomeControllerTest extends TestCase $this->assertSessionHas('warning', '91 days of data may take a while to load.'); } + /** + * @covers FireflyIII\Http\Controllers\HomeController::flush + */ public function testFlush() { $this->be($this->user()); @@ -33,12 +39,13 @@ class HomeControllerTest extends TestCase $this->assertEquals(302, $response->status()); } + /** + * @covers FireflyIII\Http\Controllers\HomeController::index + */ public function testIndex() { $this->be($this->user()); $response = $this->call('GET', '/'); $this->assertEquals(200, $response->status()); } - - } \ No newline at end of file From 3354d53a4804394e28567ac0847182b3abe9d7ff Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 18:09:53 +0100 Subject: [PATCH 216/276] Reset phpunit xml. --- pu.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pu.sh b/pu.sh index 0029a63e66..8f849a617c 100755 --- a/pu.sh +++ b/pu.sh @@ -3,6 +3,9 @@ # set testing environment cp .env.testing .env +# set default phpunit. +cp phpunit.default.xml phpunit.xml + # delete test databases: if [ -f storage/database/testing.db ] then From 9957c95c683c85e616b94b7b48924ea1f7bb9192 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 18:10:07 +0100 Subject: [PATCH 217/276] Re-order auth controller. --- app/Http/Controllers/Auth/AuthController.php | 172 +++++++++---------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index c7ec06cfcd..6fec8a12eb 100755 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -44,19 +44,6 @@ class AuthController extends Controller parent::__construct(); } - /** - * Show the application registration form. - * - * @return \Illuminate\Http\Response - */ - public function showRegistrationForm() - { - $host = Rq::getHttpHost(); - - return view('auth.register', compact('host')); - } - - /** * Handle a login request to the application. * @@ -77,6 +64,8 @@ class AuthController extends Controller $credentials['blocked'] = 0; // most not be blocked. if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) { + Session::flash('isLoggedIn', 'yes'); + return $this->handleUserWasAuthenticated($request, $throttles); } @@ -101,79 +90,6 @@ class AuthController extends Controller return $this->sendFailedLoginResponse($request, $message); } - /** - * Get the failed login response instance. - * - * @param \Illuminate\Http\Request $request - * - * @param $message - * - * @return \Illuminate\Http\Response - */ - protected function sendFailedLoginResponse(Request $request, $message) - { - return redirect()->back() - ->withInput($request->only($this->loginUsername(), 'remember')) - ->withErrors( - [ - $this->loginUsername() => $this->getFailedLoginMessage($message), - ] - ); - } - - /** - * Get the failed login message. - * - * @param $message - * - * @return string - */ - protected function getFailedLoginMessage($message) - { - if (strlen($message) > 0) { - return $message; - } - - return Lang::has('auth.failed') - ? Lang::get('auth.failed') - : 'These credentials do not match our records.'; - } - - - /** - * Get a validator for an incoming registration request. - * - * @param array $data - * - * @return \Illuminate\Contracts\Validation\Validator - */ - protected function validator(array $data) - { - return Validator::make( - $data, [ - 'email' => 'required|email|max:255|unique:users', - 'password' => 'required|confirmed|min:6', - ] - ); - } - - /** - * Create a new user instance after a valid registration. - * - * @param array $data - * - * @return User - */ - protected function create(array $data) - { - return User::create( - [ - 'email' => $data['email'], - 'password' => bcrypt($data['password']), - ] - ); - } - /** * Handle a registration request for the application. * @@ -242,6 +158,35 @@ class AuthController extends Controller return redirect($this->redirectPath()); } + /** + * Show the application registration form. + * + * @return \Illuminate\Http\Response + */ + public function showRegistrationForm() + { + $host = Rq::getHttpHost(); + + return view('auth.register', compact('host')); + } + + /** + * Create a new user instance after a valid registration. + * + * @param array $data + * + * @return User + */ + protected function create(array $data) + { + return User::create( + [ + 'email' => $data['email'], + 'password' => bcrypt($data['password']), + ] + ); + } + /** * @return array */ @@ -259,6 +204,24 @@ class AuthController extends Controller return $domains; } + /** + * Get the failed login message. + * + * @param $message + * + * @return string + */ + protected function getFailedLoginMessage($message) + { + if (strlen($message) > 0) { + return $message; + } + + return Lang::has('auth.failed') + ? Lang::get('auth.failed') + : 'These credentials do not match our records.'; + } + /** * @param $email * @@ -275,4 +238,41 @@ class AuthController extends Controller return false; } + + /** + * Get the failed login response instance. + * + * @param \Illuminate\Http\Request $request + * + * @param $message + * + * @return \Illuminate\Http\Response + */ + protected function sendFailedLoginResponse(Request $request, $message) + { + return redirect()->back() + ->withInput($request->only($this->loginUsername(), 'remember')) + ->withErrors( + [ + $this->loginUsername() => $this->getFailedLoginMessage($message), + ] + ); + } + + /** + * Get a validator for an incoming registration request. + * + * @param array $data + * + * @return \Illuminate\Contracts\Validation\Validator + */ + protected function validator(array $data) + { + return Validator::make( + $data, [ + 'email' => 'required|email|max:255|unique:users', + 'password' => 'required|confirmed|min:6', + ] + ); + } } From e8180d252baa80fae05ced4fc905b90303b0ac1b Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 18:10:30 +0100 Subject: [PATCH 218/276] Wrong URL for login form. --- resources/views/auth/password.twig | 2 +- resources/views/auth/register.twig | 2 +- resources/views/auth/reset.twig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/auth/password.twig b/resources/views/auth/password.twig index ecbc5a7750..366b0abda8 100644 --- a/resources/views/auth/password.twig +++ b/resources/views/auth/password.twig @@ -40,7 +40,7 @@ - I want to login
    + I want to login
    I forgot my password
    diff --git a/resources/views/auth/register.twig b/resources/views/auth/register.twig index c2c82b142c..95a63ff5e5 100644 --- a/resources/views/auth/register.twig +++ b/resources/views/auth/register.twig @@ -46,7 +46,7 @@
    - I want to login
    + I want to login
    I forgot my password
    diff --git a/resources/views/auth/reset.twig b/resources/views/auth/reset.twig index b4a64a9ac7..62e1f7eb4e 100644 --- a/resources/views/auth/reset.twig +++ b/resources/views/auth/reset.twig @@ -44,7 +44,7 @@ - I want to login
    + I want to login
    {% if Config.get('auth.allow_register') %} Register a new account
    {% endif %} From 37693ad08d36876bdba0a1bb5b3db4dd92617165 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 18:14:03 +0100 Subject: [PATCH 219/276] Fixed mail config. --- config/mail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/mail.php b/config/mail.php index fa74f98ae8..06cfb8d209 100755 --- a/config/mail.php +++ b/config/mail.php @@ -54,7 +54,7 @@ return [ | */ - 'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'], + 'from' => ['address' => env('MAIL_USERNAME', null), 'name' => 'Firefly III Mailer'], /* |-------------------------------------------------------------------------- From a3132657855a7bea1d0bc7571a76ff3615a43aa3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 18:14:15 +0100 Subject: [PATCH 220/276] Different URL for password reset. --- resources/views/auth/login.twig | 2 +- resources/views/auth/password.twig | 4 ++-- resources/views/auth/register.twig | 2 +- resources/views/emails/registered-html.twig | 2 +- resources/views/emails/registered.twig | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/views/auth/login.twig b/resources/views/auth/login.twig index 1257985a91..7390c87879 100644 --- a/resources/views/auth/login.twig +++ b/resources/views/auth/login.twig @@ -44,7 +44,7 @@ {% if Config.get('auth.allow_register') %} Register a new account
    {% endif %} - I forgot my password + I forgot my password
    {% endblock %} diff --git a/resources/views/auth/password.twig b/resources/views/auth/password.twig index 366b0abda8..bce754d502 100644 --- a/resources/views/auth/password.twig +++ b/resources/views/auth/password.twig @@ -23,7 +23,7 @@ {% endblock %} diff --git a/resources/views/emails/registered-html.twig b/resources/views/emails/registered-html.twig index 5c89c1a81d..11516a4c57 100644 --- a/resources/views/emails/registered-html.twig +++ b/resources/views/emails/registered-html.twig @@ -19,7 +19,7 @@
    • If you have forgotten your password already, please reset it using - the password reset tool. + the password reset tool.
    • There is a help-icon in the top right corner of each page. If you need help, click it! diff --git a/resources/views/emails/registered.twig b/resources/views/emails/registered.twig index 400ea17887..6ff499f117 100644 --- a/resources/views/emails/registered.twig +++ b/resources/views/emails/registered.twig @@ -12,7 +12,7 @@ Firefly III: {{ address }} Password reset: -{{ address }}/password/email +{{ address }}/password/reset Documentation: https://github.com/JC5/firefly-iii/wiki/First-use From 86e127ebffd6c8d9d3c689532d2e78f61d5018e6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Jan 2016 18:32:09 +0100 Subject: [PATCH 221/276] Fixed password reset routine. --- app/Http/routes.php | 18 ++++++++++++++- resources/views/auth/password.twig | 37 +++++++++++++++++++----------- resources/views/auth/reset.twig | 5 +--- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index 1b513bb540..747682d5c2 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -3,7 +3,23 @@ // auth routes, i think Route::group( ['middleware' => 'web'], function () { - Route::auth(); + + // Authentication Routes... + Route::get('/login', 'Auth\AuthController@showLoginForm'); + Route::post('/login', 'Auth\AuthController@login'); + Route::get('/logout', 'Auth\AuthController@logout'); + + // Registration Routes... + Route::get('/register', ['uses' => 'Auth\AuthController@showRegistrationForm', 'as' => 'register']); + Route::post('/register', 'Auth\AuthController@register'); + + Route::get('/password/reset', 'Auth\PasswordController@getReset'); + + // Password Reset Routes... + Route::get('/password/reset/{token?}', 'Auth\PasswordController@showResetForm'); + Route::post('/password/email', 'Auth\PasswordController@sendResetLinkEmail'); + Route::post('/password/reset', 'Auth\PasswordController@reset'); + //Route::get('/home', 'HomeController@index'); } diff --git a/resources/views/auth/password.twig b/resources/views/auth/password.twig index bce754d502..20323c501f 100644 --- a/resources/views/auth/password.twig +++ b/resources/views/auth/password.twig @@ -20,25 +20,34 @@
    {% endif %} + +