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) }) }} +++ {{ Form.close|raw }} +{% endblock %}++++++{{ trans('form.delete_rule', {'title': rule.title}) }}
+++ ++ {{ trans('form.permDeleteWarning') }} +
+ ++ {{ trans('form.rule_areYouSure', {'title': rule.title}) }} +
+