diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index abbc81b0b4..0b0ba06038 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -12,6 +12,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Preferences; use Response; @@ -180,6 +181,23 @@ class JsonController extends Controller } + /** + * Returns a JSON list of all beneficiaries. + * + * @return \Illuminate\Http\JsonResponse + */ + public function tags(TagRepositoryInterface $tagRepository) + { + $list = $tagRepository->get(); + $return = []; + foreach ($list as $entry) { + $return[] = $entry->tag; + } + + return Response::json($return); + + } + /** * @return \Illuminate\Http\JsonResponse */ diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index d03fac2456..ba15b3ea83 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -62,11 +62,23 @@ class TagController extends Controller return view('tags.create', compact('subTitle', 'subTitleIcon')); } + /** + * @param Tag $tag + * + * @return View + */ public function edit(Tag $tag) { $subTitle = 'Edit tag "' . e($tag->tag) . '"'; $subTitleIcon = 'fa-tag'; + // put previous url in session if not redirect from store (not "return_to_edit"). + if (Session::get('tags.edit.fromUpdate') !== true) { + Session::put('tags.edit.url', URL::previous()); + } + Session::forget('tags.edit.fromUpdate'); + + return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon')); } @@ -128,13 +140,13 @@ class TagController extends Controller $data = [ 'tag' => $request->get('tag'), 'date' => strlen($request->get('date')) > 0 ? new Carbon($request->get('date')) : null, - 'description' => strlen($request->get('description')) > 0 ? $request->get('description') : null, + 'description' => strlen($request->get('description')) > 0 ? $request->get('description') : '', 'latitude' => $latitude, 'longitude' => $longitude, 'zoomLevel' => $zoomLevel, 'tagMode' => $request->get('tagMode'), ]; - $tag = $repository->store($data); + $repository->store($data); Session::flash('success','The tag has been created!'); @@ -149,4 +161,43 @@ class TagController extends Controller return Redirect::to(Session::get('tags.create.url')); } + + /** + * @param Tag $tag + */ + public function update(Tag $tag, TagFormRequest $request, TagRepositoryInterface $repository) { + if (Input::get('setTag') == 'true') { + $latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null; + $longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null; + $zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null; + } else { + $latitude = null; + $longitude = null; + $zoomLevel = null; + } + + $data = [ + 'tag' => $request->get('tag'), + 'date' => strlen($request->get('date')) > 0 ? new Carbon($request->get('date')) : null, + 'description' => strlen($request->get('description')) > 0 ? $request->get('description') : '', + 'latitude' => $latitude, + 'longitude' => $longitude, + 'zoomLevel' => $zoomLevel, + 'tagMode' => $request->get('tagMode'), + ]; + + $repository->update($tag, $data); + + Session::flash('success', 'Tag "' . e($data['tag']) . '" updated.'); + + if (intval(Input::get('return_to_edit')) === 1) { + // set value so edit routine will not overwrite URL: + Session::put('tags.edit.fromUpdate', true); + + return Redirect::route('tags.edit', $tag->id)->withInput(['return_to_edit' => 1]); + } + + // redirect to previous URL. + return Redirect::to(Session::get('tags.edit.url')); + } } \ No newline at end of file diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 5a3bbfa628..87051ad93a 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -134,6 +134,12 @@ class TransactionController extends Controller 'budget_id' => 0, 'piggy_bank_id' => 0 ]; + // get tags: + $tags = []; + foreach ($journal->tags as $tag) { + $tags[] = $tag->tag; + } + $preFilled['tags'] = join(',', $tags); $category = $journal->categories()->first(); if (!is_null($category)) { @@ -156,6 +162,8 @@ class TransactionController extends Controller $preFilled['account_from_id'] = $transactions[1]->account->id; $preFilled['account_to_id'] = $transactions[0]->account->id; + Session::flash('preFilled',$preFilled); + // put previous url in session if not redirect from store (not "return_to_edit"). if (Session::get('transactions.edit.fromUpdate') !== true) { Session::put('transactions.edit.url', URL::previous()); @@ -271,6 +279,7 @@ class TransactionController extends Controller public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) { + $journalData = $request->getJournalData(); $journal = $repository->store($journalData); diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index b0da081e4a..1bae744518 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -42,6 +42,7 @@ class JournalFormRequest extends Request 'date' => new Carbon($this->get('date')), 'budget_id' => intval($this->get('budget_id')), 'category' => $this->get('category'), + 'tags' => explode(',', $this->get('tags')), ]; } diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index 8c7d1ae7c0..f82b055478 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -9,6 +9,8 @@ namespace FireflyIII\Http\Requests; use Auth; +use FireflyIII\Models\Tag; +use Input; /** * Class TagFormRequest @@ -31,8 +33,16 @@ class TagFormRequest extends Request */ public function rules() { + $idRule = ''; + $tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,TRUE'; + if (Tag::find(Input::get('id'))) { + $idRule = 'belongsToUser:tags'; + $tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,TRUE,' . Input::get('id'); + } + return [ - 'tag' => 'required|min:1|uniqueObjectForUser:tags,tag,TRUE', + 'tag' => $tagRule, + 'id' => $idRule, 'description' => 'min:1', 'date' => 'date', 'latitude' => 'numeric|min:-90|max:90', diff --git a/app/Http/routes.php b/app/Http/routes.php index e6d4b71aa3..21f2feeaec 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -256,6 +256,7 @@ Route::group( Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']); Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']); Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']); + Route::get('/json/tags', ['uses' => 'JsonController@tags', 'as' => 'json.tags']); Route::get('/json/box/in', ['uses' => 'JsonController@boxIn', 'as' => 'json.box.in']); Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']); Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']); diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 6dcc4353ce..d965150c0a 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -2,8 +2,10 @@ namespace FireflyIII\Models; +use App; use Crypt; use Illuminate\Database\Eloquent\Model; +use Watson\Validating\ValidatingTrait; /** * Class Tag @@ -12,8 +14,57 @@ use Illuminate\Database\Eloquent\Model; */ class Tag extends Model { + use ValidatingTrait; - protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude','zoomLevel','tagMode']; + protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode']; + protected $rules + = [ + 'tag' => 'required|min:1|uniqueObjectForUser:tags,tag,TRUE', + 'description' => 'min:1', + 'date' => 'date', + 'latitude' => 'numeric|min:-90|max:90', + 'longitude' => 'numeric|min:-90|max:90', + 'tagMode' => 'required|in:nothing,balancingAct,advancePayment' + ]; + + /** + * @param array $fields + * + * @return Tag|null + */ + public static function firstOrCreateEncrypted(array $fields) + { + // everything but the tag: + if (isset($fields['tagMode'])) { + unset($fields['tagMode']); + } + $query = Tag::orderBy('id'); + foreach ($fields as $name => $value) { + if ($name != 'tag') { + $query->where($name, $value); + } + } + $set = $query->get(['tags.*']); + /** @var Tag $tag */ + foreach ($set as $tag) { + if ($tag->tag == $fields['tag']) { + return $tag; + } + } + // create it! + $fields['tagMode'] = 'nothing'; + $fields['description'] = isset($fields['description']) && !is_null($fields['description']) ? $fields['description'] : ''; + $tag = Tag::create($fields); + if (is_null($tag->id)) { + // could not create account: + App::abort(500, 'Could not create new tag with data: ' . json_encode($fields) . ' because ' . json_encode($tag->getErrors())); + + + } + + return $tag; + + } /** * @return array @@ -33,14 +84,6 @@ class Tag extends Model return Crypt::decrypt($value); } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany - */ - public function transactionjournals() - { - return $this->belongsToMany('FireflyIII\Models\TransactionJournal'); - } - /** * @param $value * @@ -67,6 +110,14 @@ class Tag extends Model $this->attributes['tag'] = Crypt::encrypt($value); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function transactionjournals() + { + return $this->belongsToMany('FireflyIII\Models\TransactionJournal'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index f8732d7867..55fd78d580 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -4,10 +4,12 @@ namespace FireflyIII\Repositories\Journal; use App; use Auth; +use DB; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; +use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -82,6 +84,20 @@ class JournalRepository implements JournalRepositoryInterface return TransactionType::whereType($type)->first(); } + /** + * @param TransactionJournal $journal + * @param array $array + * + * @return void + */ + public function saveTags(TransactionJournal $journal, array $array) + { + foreach ($array as $name) { + $tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]); + $journal->tags()->save($tag); + } + } + /** * @param string $query * @param TransactionJournal $journal @@ -157,6 +173,10 @@ class JournalRepository implements JournalRepositoryInterface ); $journal->save(); + if (isset($data['tags']) && is_array($data['tags'])) { + $this->saveTags($journal, $data['tags']); + } + // store or get category if (strlen($data['category']) > 0) { @@ -246,9 +266,44 @@ class JournalRepository implements JournalRepositoryInterface $journal->save(); + // update tags: + if (isset($data['tags']) && is_array($data['tags'])) { + $this->updateTags($journal, $data['tags']); + } + return $journal; } + /** + * @param TransactionJournal $journal + * @param array $array + * + * @return void + */ + public function updateTags(TransactionJournal $journal, array $array) + { + // find or create all tags: + $tags = []; + $ids = []; + foreach ($array as $name) { + $tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]); + $tags[] = $tag; + $ids[] = $tag->id; + } + + // delete all tags connected to journal not in this array: + if (count($ids) > 0) { + DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete(); + } + + // connect each tag to journal (if not yet connected): + foreach($tags as $tag) { + if(!$journal->tags()->find($tag->id)) { + $journal->tags()->save($tag); + } + } + } + /** * @param TransactionType $type * @param array $data diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index 02febb7b96..af608699fe 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -31,6 +31,23 @@ interface JournalRepositoryInterface */ public function getAssetAccount(TransactionJournal $journal); + /** + * @param TransactionJournal $journal + * @param array $array + * + * @return void + */ + public function updateTags(TransactionJournal $journal, array $array); + + /** + * @param TransactionJournal $journal + * @param array $array + * + * @return void + */ + public function saveTags(TransactionJournal $journal, array $array); + + /** * @param TransactionType $dbType * diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 8bd8accd7d..030813b8a6 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -5,6 +5,7 @@ namespace FireflyIII\Repositories\Tag; use Auth; use FireflyIII\Models\Tag; +use Illuminate\Support\Collection; /** * Class TagRepository @@ -14,6 +15,22 @@ use FireflyIII\Models\Tag; class TagRepository implements TagRepositoryInterface { + /** + * @return Collection + */ + public function get() + { + /** @var Collection $tags */ + $tags = Auth::user()->tags()->get(); + $tags->sortBy( + function (Tag $tag) { + return $tag->tag; + } + ); + + return $tags; + } + /** * @param array $data * @@ -28,7 +45,7 @@ class TagRepository implements TagRepositoryInterface $tag->latitude = $data['latitude']; $tag->longitude = $data['longitude']; $tag->zoomLevel = $data['zoomLevel']; - $tag->tagMode = $data['tagMode']; + $tag->tagMode = $data['tagMode']; $tag->user()->associate(Auth::user()); $tag->save(); @@ -36,4 +53,22 @@ class TagRepository implements TagRepositoryInterface } + + /** + * @param Tag $tag + * @param array $data + * + * @return Tag + */ + public function update(Tag $tag, array $data) { + $tag->tag = $data['tag']; + $tag->date = $data['date']; + $tag->description = $data['description']; + $tag->latitude = $data['latitude']; + $tag->longitude = $data['longitude']; + $tag->zoomLevel = $data['zoomLevel']; + $tag->tagMode = $data['tagMode']; + $tag->save(); + return $tag; + } } \ No newline at end of file diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index a6d697be89..124aa88707 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -2,6 +2,7 @@ namespace FireflyIII\Repositories\Tag; use FireflyIII\Models\Tag; +use Illuminate\Support\Collection; /** @@ -17,4 +18,17 @@ interface TagRepositoryInterface { * @return Tag */ public function store(array $data); + + /** + * @return Collection + */ + public function get(); + + /** + * @param Tag $tag + * @param array $data + * + * @return Tag + */ + public function update(Tag $tag, array $data); } \ No newline at end of file diff --git a/public/js/bootstrap-tagsinput.min.js.map b/public/js/bootstrap-tagsinput.min.js.map new file mode 100755 index 0000000000..a0d198d16b --- /dev/null +++ b/public/js/bootstrap-tagsinput.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dist/bootstrap-tagsinput-angular.min.js","sources":["src/bootstrap-tagsinput-angular.js"],"names":["angular","module","directive","getItemProperty","scope","property","isFunction","$parent","item","undefined","restrict","model","template","replace","link","element","attrs","$","isArray","select","typeaheadSourceArray","typeaheadSource","split","length","tagsinput","options","typeahead","source","itemValue","itemvalue","itemText","itemtext","confirmKeys","confirmkeys","JSON","parse","tagClass","tagclass","i","on","event","indexOf","push","idx","splice","prev","slice","$watch","added","filter","removed"],"mappings":";;;;;AAAAA,QAAQC,OAAO,0BACdC,UAAU,sBAAuB,WAEhC,QAASC,GAAgBC,EAAOC,GAC9B,MAAKA,GAGDL,QAAQM,WAAWF,EAAMG,QAAQF,IAC5BD,EAAMG,QAAQF,GAEhB,SAASG,GACd,MAAOA,GAAKH,IANLI,OAUX,OACEC,SAAU,KACVN,OACEO,MAAO,YAETC,SAAU,6BACVC,SAAS,EACTC,KAAM,SAASV,EAAOW,EAASC,GAC7BC,EAAE,WACKjB,QAAQkB,QAAQd,EAAMO,SACzBP,EAAMO,SAER,IAAIQ,GAASF,EAAE,SAAUF,GACrBK,EAAuBJ,EAAMK,gBAAkBL,EAAMK,gBAAgBC,MAAM,KAAO,KAClFD,EAAkBD,EACjBA,EAAqBG,OAAS,EAC3BnB,EAAMG,QAAQa,EAAqB,IAAIA,EAAqB,IAC1DhB,EAAMG,QAAQa,EAAqB,IACvC,IAEND,GAAOK,UAAUpB,EAAMG,QAAQS,EAAMS,SAAW,MAC9CC,WACEC,OAAW3B,QAAQM,WAAWe,GAAmBA,EAAkB,MAErEO,UAAWzB,EAAgBC,EAAOY,EAAMa,WACxCC,SAAW3B,EAAgBC,EAAOY,EAAMe,UACxCC,YAAc7B,EAAgBC,EAAOY,EAAMiB,aAAeC,KAAKC,MAAMnB,EAAMiB,cAAgB,IAC3FG,SAAWpC,QAAQM,WAAWF,EAAMG,QAAQS,EAAMqB,WAAajC,EAAMG,QAAQS,EAAMqB,UAAY,WAAiB,MAAOrB,GAAMqB,WAG/H,KAAK,GAAIC,GAAI,EAAGA,EAAIlC,EAAMO,MAAMY,OAAQe,IACtCnB,EAAOK,UAAU,MAAOpB,EAAMO,MAAM2B,GAGtCnB,GAAOoB,GAAG,YAAa,SAASC,GACU,KAApCpC,EAAMO,MAAM8B,QAAQD,EAAMhC,OAC5BJ,EAAMO,MAAM+B,KAAKF,EAAMhC,QAG3BW,EAAOoB,GAAG,cAAe,SAASC,GAChC,GAAIG,GAAMvC,EAAMO,MAAM8B,QAAQD,EAAMhC,KACxB,MAARmC,GACFvC,EAAMO,MAAMiC,OAAOD,EAAK,IAK5B,IAAIE,GAAOzC,EAAMO,MAAMmC,OACvB1C,GAAM2C,OAAO,QAAS,WACpB,GAEIT,GAFAU,EAAQ5C,EAAMO,MAAMsC,OAAO,SAASX,GAAI,MAA2B,KAApBO,EAAKJ,QAAQH,KAC5DY,EAAUL,EAAKI,OAAO,SAASX,GAAI,MAAkC,KAA3BlC,EAAMO,MAAM8B,QAAQH,IAMlE,KAHAO,EAAOzC,EAAMO,MAAMmC,QAGdR,EAAI,EAAGA,EAAIY,EAAQ3B,OAAQe,IAC9BnB,EAAOK,UAAU,SAAU0B,EAAQZ,GAOrC,KAHAnB,EAAOK,UAAU,WAGZc,EAAI,EAAGA,EAAIU,EAAMzB,OAAQe,IAC5BnB,EAAOK,UAAU,MAAOwB,EAAMV,MAE/B"} \ No newline at end of file diff --git a/public/js/transactions.js b/public/js/transactions.js index 9d918bef90..20e7b6eaee 100644 --- a/public/js/transactions.js +++ b/public/js/transactions.js @@ -8,6 +8,20 @@ $(document).ready(function () { $('input[name="expense_account"]').typeahead({source: data}); }); } + + if ($('input[name="tags"]').length > 0) { + $.getJSON('json/tags').success(function (data) { + var opt = { + typeahead: { + source: data + } + }; + $('input[name="tags"]').tagsinput( + opt + ); + }); + } + if ($('input[name="revenue_account"]').length > 0) { $.getJSON('json/revenue-accounts').success(function (data) { $('input[name="revenue_account"]').typeahead({source: data}); diff --git a/resources/views/list/journals-full.blade.php b/resources/views/list/journals-full.blade.php index 8e671328f8..c44921e135 100644 --- a/resources/views/list/journals-full.blade.php +++ b/resources/views/list/journals-full.blade.php @@ -35,7 +35,7 @@
diff --git a/resources/views/tags/edit.blade.php b/resources/views/tags/edit.blade.php index 1ebe1d5149..01a4709e00 100644 --- a/resources/views/tags/edit.blade.php +++ b/resources/views/tags/edit.blade.php @@ -3,6 +3,7 @@ {!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $tag) !!} {!! Form::model($tag, ['class' => 'form-horizontal','id' => 'update','url' => route('tags.update',$tag->id)]) !!} +