V2 First Push

This commit is contained in:
David Bomba 2019-03-26 14:08:19 +11:00
parent 0d508d67f1
commit 9ff835a52e
49 changed files with 10 additions and 7215 deletions

View File

@ -1,5 +0,0 @@
<?php
return [
'name' => 'Notes'
];

View File

@ -1,8 +0,0 @@
<?php
return [
'client' => [
'notes' => ["hasMany", ['Modules\Notes\Entities\Note']]
],
];

View File

@ -1,38 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class NotesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('notes', function ($table) {
$table->increments('id');
$table->unsignedInteger('client_id')->index();
$table->unsignedInteger('user_id')->index();
$table->unsignedInteger('company_id')->index();
$table->string('description');
$table->timestamps();
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Modules\Notes\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class NotesDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace Modules\Notes\Entities;
use Illuminate\Database\Eloquent\Model;
class Note extends Model
{
/*
protected $guarded = [
'id',
];
*/
protected $fillable = ["description"];
protected $table = 'notes';
public function client()
{
return $this->hasOne(App\Models\Client::class);
}
public function notes()
{
return $this->hasMany(Note::class);
}
}

View File

@ -1,117 +0,0 @@
<?php
namespace Modules\Notes\Http\Controllers;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use Modules\Notes\Entities\Note;
use Yajra\DataTables\Facades\DataTables;
use Yajra\DataTables\Html\Builder;
class NotesController extends Controller
{
use UserSessionAttributes;
use MakesHash;
/**
* Display a listing of the resource.
* @return Response
*/
public function index(Builder $builder)
{
if (request()->ajax()) {
$notes = Note::query()->where('company_id', '=', $this->getCurrentCompanyId());
return DataTables::of($notes->get())
->addColumn('created_at', function ($note) {
return $note->created_at;
})
->addColumn('description', function ($note) {
return $note->description;
})
->addColumn('action', function ($note) {
return '<a href="/notes/'. $this->encodePrimaryKey($note->id) .'/edit" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-edit"></i> Edit</a>';
})
->addColumn('checkbox', function ($note){
return '<input type="checkbox" name="bulk" value="'. $note->id .'"/>';
})
->rawColumns(['checkbox', 'action'])
->make(true);
}
$builder->addAction();
$builder->addCheckbox();
$html = $builder->columns([
['data' => 'created_at', 'name' => 'checkbox', 'title' => '', 'searchable' => false, 'orderable' => false],
['data' => 'description', 'name' => 'name', 'title' => trans('texts.name'), 'visible'=> true],
['data' => 'action', 'name' => 'action', 'title' => '', 'searchable' => false, 'orderable' => false],
]);
$builder->ajax([
'url' => route('notes.index'),
'type' => 'GET',
'data' => 'function(d) { d.key = "value"; }',
]);
$data['html'] = $html;
return view('notes::index', $data);
}
/**
* Show the form for creating a new resource.
* @return Response
*/
public function create()
{
return view('notes::create');
}
/**
* Store a newly created resource in storage.
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
}
/**
* Show the specified resource.
* @return Response
*/
public function show()
{
return view('notes::show');
}
/**
* Show the form for editing the specified resource.
* @return Response
*/
public function edit()
{
return view('notes::edit');
}
/**
* Update the specified resource in storage.
* @param Request $request
* @return Response
*/
public function update(Request $request)
{
}
/**
* Remove the specified resource from storage.
* @return Response
*/
public function destroy()
{
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace Modules\Notes\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateNoteRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace Modules\Notes\Http\ViewComposers;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\View\View;
class ClientEditComposer
{
use UserSessionAttributes;
/**
* Bind data to the view.
*
* @param View $view
* @return void
*/
public function compose(View $view)
{
$data = $view->getData();
$view->with('notes::edit', $this->clientEditData);
}
private function clientEditData()
{
}

View File

@ -1,20 +0,0 @@
<?php
namespace Modules\Notes\Policies;
use Illuminate\Auth\Access\HandlesAuthorization;
class NotePolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* @return void
*/
public function __construct()
{
//
}
}

View File

@ -1,117 +0,0 @@
<?php
namespace Modules\Notes\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
class NotesServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Boot the application events.
*
* @return void
*/
public function boot()
{
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->registerFactories();
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->register(RouteServiceProvider::class);
}
/**
* Register config.
*
* @return void
*/
protected function registerConfig()
{
$this->publishes([
__DIR__.'/../Config/config.php' => config_path('modules.notes' . '.php'),
], 'config');
$this->mergeConfigFrom(__DIR__.'/../Config/relations.php', 'modules.relations');
$this->mergeConfigFrom(
__DIR__.'/../Config/config.php', 'notes'
);
}
/**
* Register views.
*
* @return void
*/
public function registerViews()
{
$viewPath = resource_path('views/modules/notes');
$sourcePath = __DIR__.'/../Resources/views';
$this->publishes([
$sourcePath => $viewPath
],'views');
$this->loadViewsFrom(array_merge(array_map(function ($path) {
return $path . '/modules/notes';
}, \Config::get('view.paths')), [$sourcePath]), 'notes');
}
/**
* Register translations.
*
* @return void
*/
public function registerTranslations()
{
$langPath = resource_path('lang/modules/notes');
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, 'notes');
} else {
$this->loadTranslationsFrom(__DIR__ .'/../Resources/lang', 'notes');
}
}
/**
* Register an additional directory of factories.
*
* @return void
*/
public function registerFactories()
{
if (! app()->environment('production')) {
app(Factory::class)->load(__DIR__ . '/../Database/factories');
}
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
}

View File

@ -1,69 +0,0 @@
<?php
namespace Modules\Notes\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
*
* @var string
*/
protected $namespace = 'Modules\Notes\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(__DIR__ . '/../Routes/web.php');
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(__DIR__ . '/../Routes/api.php');
}
}

View File

@ -1,7 +0,0 @@
<?php
return [
'new_note' => 'New Note',
];
?>

View File

@ -1,13 +0,0 @@
<div class="container-fluid">
@if($client)
<span>{{ $client->name }} </span>
@endif
<ul>
@foreach($client->notes()->get() as $note)
<li> {{ $note->description }} </li>
@endforeach
</ul>
</div>

View File

@ -1,41 +0,0 @@
@extends('layouts.master', ['header' => $header])
@section('head')
@parent
<link rel="stylesheet" href="//cdn.datatables.net/1.10.18/css/dataTables.bootstrap4.min.css">
<script src="//cdn.datatables.net/1.10.18/js/jquery.dataTables.min.js"></script>
<script src="//cdn.datatables.net/1.10.18/js/dataTables.bootstrap4.min.js"></script>
@endsection
@section('body')
@parent
<main class="main" >
<!-- Breadcrumb-->
{{ Breadcrumbs::render('clients') }}
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<button class="btn btn-primary btn-lg pull-right">{{ trans('notes::texts.new_note') }}</button>
</div>
</div>
<div id="ui-view">
<div class="animated fadeIn" style="padding-top:20px;">
<div class="row col-md-12 card">
{!! $html->table() !!}
</div>
</div>
</div>
</div>
</main>
@endsection
@section('footer')
@parent
{!! $html->scripts() !!}
@endsection

View File

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Module Notes</title>
{{-- Laravel Mix - CSS File --}}
{{-- <link rel="stylesheet" href="{{ mix('css/notes.css') }}"> --}}
</head>
<body>
@yield('content')
{{-- Laravel Mix - JS File --}}
{{-- <script src="{{ mix('js/notes.js') }}"></script> --}}
</body>
</html>

View File

@ -1,18 +0,0 @@
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
Route::middleware('auth:api')->get('/notes', function (Request $request) {
return $request->user();
});
*/

View File

@ -1,16 +0,0 @@
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group(['middleware' => ['auth:user', 'db'], 'prefix' => 'notes'], function() {
Route::get('/', 'NotesController@index')->name('notes.index');
});

View File

@ -1,80 +0,0 @@
<?php
namespace Modules\Notes\Tests;
use App\Models\Client;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Nwidart\Modules\Facades\Module;
use Tests\TestCase;
class CheckMenuModulesTest extends TestCase
{
public function setUp()
{
parent::setUp();
}
/**
* A basic test example.
*
* @return void
*/
public function testModulesAreDetected()
{
$this->assertGreaterThan(0, Module::count());
}
public function testNotesModuleExists()
{
$module = Module::find('Notes');
$this->assertNotNull($module);
}
public function testNoSideBarVariableExists()
{
$module = Module::find('Notes');
$this->assertNotNull($module->get('sidebar'));
}
public function testViewsVariableExistsAndIsArray()
{
$module = Module::find('Notes');
$this->assertTrue(is_array($module->get('views')));
}
public function testViewsVariableExistsAndContainsClients()
{
$module = Module::find('Notes');
$array = $module->get('views');
$this->assertTrue(in_array('client', $array));
}
public function testViewsVariableExistsAndDoesNotContainRandomObject()
{
$module = Module::find('Notes');
$array = $module->get('views');
$this->assertFalse(in_array('foo', $array));
}
public function testResolvingTabMenuCorrectly()
{
$entity = Client::class;
$tabs = [];
foreach (Module::getCached() as $module)
{
if(!$module['sidebar']
&& $module['active'] == 1
&& in_array( strtolower( class_basename($entity) ), $module['views']))
{
$tabs[] = $module;
}
}
$this->assertFalse($module['sidebar']);
$this->assertEquals(1,$module['active']);
$this->assertEquals('client', strtolower(class_basename(Client::class)));
$this->assertTrue( in_array(strtolower(class_basename(Client::class)), $module['views']) );
$this->assertEquals(1, count($tabs));
}
}

View File

@ -1,30 +0,0 @@
{
"name": "invoiceninja/notes",
"description": "",
"authors": [
{
"name": "David Bomba",
"email": "david@invoiceninja.com"
}
],
"extra": {
"laravel": {
"providers": [
"Modules\\Notes\\Providers\\NotesServiceProvider"
],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Notes\\": ""
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "Tests/"
}
},
}

View File

@ -1,19 +0,0 @@
{
"name": "Notes",
"alias": "notes",
"description": "Generic Notes Module",
"keywords": [],
"active": 1,
"order": 0,
"providers": [
"Modules\\Notes\\Providers\\NotesServiceProvider"
],
"aliases": [],
"icon": "bell",
"plural": "notes",
"base-route": "notes",
"settings": false,
"sidebar": false,
"requires": [],
"views": ["client","invoice"]
}

View File

@ -1,17 +0,0 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"cross-env": "^5.1.4",
"laravel-mix": "^2.1",
"laravel-mix-merge-manifest": "^0.1.1"
}
}

View File

@ -1,11 +0,0 @@
const { mix } = require('laravel-mix');
require('laravel-mix-merge-manifest');
mix.setPublicPath('../../public').mergeManifest();
mix.js(__dirname + '/Resources/assets/js/app.js', 'js/notes.js')
.sass( __dirname + '/Resources/assets/sass/app.scss', 'css/notes.css');
if (mix.inProduction()) {
mix.version();
}

View File

@ -2,11 +2,11 @@
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
</p>
[![Build Status](https://travis-ci.org/invoiceninja/invoiceninja.svg?branch=v5.0)](https://travis-ci.org/invoiceninja/invoiceninja)
[![Build Status](https://travis-ci.org/invoiceninja/invoiceninja.svg?branch=v2)](https://travis-ci.org/invoiceninja/invoiceninja)
[![codecov](https://codecov.io/gh/invoiceninja/invoiceninja/branch/v5.0/graph/badge.svg)](https://codecov.io/gh/invoiceninja/invoiceninja)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=invoiceninja/invoiceninja&amp;utm_campaign=Badge_Grade)
**Invoice Ninja v 2.0** is coming soon!
**Invoice Ninja v 2** is coming soon!
We will be using the lessons learnt in Invoice Ninja 4.0 to build a bigger better platform to work from. If you would like to contribute to the project we will gladly accept contributions for code, user guides, bug tracking and feedback! Please consider the following guidelines prior to submitting a pull request:

View File

@ -33,21 +33,6 @@ class DefaultSettings extends BaseSettings
{
return (object)[
'datatable' => (object) [
'per_page' => self::$per_page,
'column_visibility' => (object)[
'name' => true,
'contact' => true,
'email' => true,
'client_created_at' => true,
'last_login' => true,
'balance' => true,
'custom_value1' => false,
'custom_value2' => true,
'custom_value3' => false,
'custom_value4' => false,
]
]
];
}

View File

@ -1,217 +0,0 @@
<?php
namespace App\Datatables;
use App\Datatables\MakesActionMenu;
use App\Filters\ClientFilters;
use App\Models\Client;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/**
* Class ClientDatatable
* @package App\Datatables
*/
class ClientDatatable extends EntityDatatable
{
use MakesHash;
use MakesActionMenu;
/**
* @var ClientFilters
*/
protected $filter;
/**
* ClientDatatable constructor.
* @param ClientFilters $filter
*/
public function __construct(ClientFilters $filter)
{
$this->filter = $filter;
}
/**
* Returns paginated results for the datatable
*
*/
public function query(Request $request, int $company_id)
{
$data = $this->filter->apply($company_id, auth()->user())->paginate($request->input('per_page'));
return response()->json($this->buildActionColumn($data), 200);
}
/**
* Returns the action dropdown menu
*
* @param $rows Std Class of client datatable rows
* @return object Rendered action column items
*/
private function buildActionColumn($rows)
{
$requested_actions = [
'view_client_client_id',
'edit_client_client_id',
'create_task_client_id',
'create_invoice_client_id',
'create_payment_client_id',
'create_credit_client_id',
'create_expense_client_id'
];
/*
* Build a collection of action
*/
$rows = $this->processActionsForDatatable($requested_actions, $rows, Client::class);
/*
* Add a _view_ link directly to the client
*/
$rows->map(function($row){
$row->name = '<a href="' . route('clients.show', ['id' => $this->encodePrimaryKey($row->id)]) . '">' . $row->name . '</a>';
return $row;
});
return $rows;
}
/**
* Returns a collection of helper fields
* for the Client List Datatable
*
* @return Collection collection
*/
public function listActions() : Collection
{
return collect([
'multi_select' => [
['name' => ctrans('texts.active'), 'value' => 'active'],
['name' => ctrans('texts.archived'), 'value' => 'archived'],
['name' => ctrans('texts.deleted'), 'value' => 'deleted']
],
'create_entity' => [
'create_permission' => auth()->user()->can('create', Client::class),
'url' => route('clients.create'),
'name' => ctrans('texts.new_client')
]
]);
}
/**
* Returns the Datatable settings including column visibility
*
* @return Collection collection
*/
public function buildOptions() : Collection
{
$visible = auth()->user()->getColumnVisibility(Client::class);
$company = auth()->user()->company();
return collect([
'per_page' => 25,
'sort_order' => [
[
'field' => 'name',
'sortField' => 'name',
'direction' => 'asc',
]
],
'fields' => [
[
'name' => '__checkbox',
'title' => '',
'titleClass' => 'center aligned',
'visible' => true,
'dataClass' => 'center aligned'
],
[
'name' => 'name',
'title' => ctrans('texts.name'),
'sortField' => 'name',
'visible' => $visible->name,
'dataClass' => 'center aligned'
],
[
'name' => 'contact',
'title' => ctrans('texts.contact'),
'sortField' => 'contact',
'visible' => $visible->contact,
'dataClass' => 'center aligned'
],
[
'name' => 'email',
'title' => ctrans('texts.email'),
'sortField' => 'email',
'visible' => $visible->email,
'dataClass' => 'center aligned'
],
[
'name' => 'client_created_at',
'title' => ctrans('texts.date_created'),
'sortField' => 'client_created_at',
'visible' => $visible->client_created_at,
'dataClass' => 'center aligned'
],
[
'name' => 'last_login',
'title' => ctrans('texts.last_login'),
'sortField' => 'last_login',
'visible' => $visible->last_login,
'dataClass' => 'center aligned'
],
[
'name' => 'balance',
'title' => ctrans('texts.balance'),
'sortField' => 'balance',
'visible' => $visible->balance,
'dataClass' => 'center aligned'
],
[
'name' => 'custom_value1',
'title' => $company->custom_client_label1 ?: '',
'sortField' => 'custom_value1',
'visible' => $visible->custom_value1,
'dataClass' => 'center aligned'
],
[
'name' => 'custom_value2',
'title' => $company->custom_client_label2 ?: '',
'sortField' => 'custom_value2',
'visible' => $visible->custom_value2,
'dataClass' => 'center aligned'
],
[
'name' => 'custom_value3',
'title' => $company->custom_client_label3 ?: '',
'sortField' => 'custom_value3',
'visible' => $visible->custom_value3,
'dataClass' => 'center aligned'
],
[
'name' => 'custom_value4',
'title' => $company->custom_client_label4 ?: '',
'sortField' => 'custom_value4',
'visible' => $visible->custom_value4,
'dataClass' => 'center aligned'
],
[
'name' => '__component:client-actions',
'title' => '',
'titleClass' => 'center aligned',
'visible' => true,
'dataClass' => 'center aligned'
]
]
]);
}
}

View File

@ -1,15 +0,0 @@
<?php
namespace App\Datatables;
/**
* Class EntityDatatable
* @package App\Datatables
*/
class EntityDatatable
{
}

View File

@ -1,156 +0,0 @@
<?php
namespace App\Datatables;
use Illuminate\Support\Collection;
/**
* Class MakesActionMenu
* @package App\Datatables
*/
trait MakesActionMenu
{
/**
* Returns all possible datatable actions
* this list will be the single source of truth
* for all Select Actions
*
* @return Collection collection instance of action items
*/
public function actions() :Collection
{
return collect([
['action' => 'view_client_client_id', 'permission' => 'view_client', 'route' => 'clients.show', 'key' => 'client_id', 'name' => ctrans('texts.view')],
['action' => 'edit_client_client_id', 'permission' => 'edit_client', 'route' => 'clients.edit', 'key' => 'client_id', 'name' => ctrans('texts.edit')],
['action' => 'create_task_client_id', 'permission' => 'create_task', 'route' => 'tasks.create', 'key' => 'client_id', 'name' => ctrans('texts.new_task')],
['action' => 'create_invoice_client_id', 'permission' => 'create_invoice', 'route' => 'invoices.create', 'key' => 'client_id', 'name' => ctrans('texts.new_invoice')],
['action' => 'create_quote_client_id', 'permission' => 'create_quote', 'route' => 'quotes.create', 'key' => 'client_id', 'name' => ctrans('texts.new_quote')],
['action' => 'create_recurring_invoice_client_id', 'permission' => 'create_recurring_invoice', 'route' => 'recurring_invoices.create', 'key' => 'client_id', 'name' => ctrans('texts.new_recurring_invoice')],
['action' => 'enter_payment_client_id', 'permission' => 'create_payment', 'route' => 'payments.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_payment')],
['action' => 'enter_credit_client_id', 'permission' => 'create_credit', 'route' => 'credits.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_credit')],
['action' => 'enter_expense_client_id', 'permission' => 'create_expense', 'route' => 'expenses.create', 'key' => 'client_id', 'name' => ctrans('texts.enter_expense')],
['action' => 'view_statement_client_id', 'permission' => 'edit_client', 'route' => 'client_statement.show', 'key' => 'client_id', 'name' => ctrans('texts.view_statement')],
]);
}
/**
* To allow fine grained permissions we need to push the rows through a
* permissions/actions sieve.
*
* Complicating the calculation is the fact we allow a user who has
* create_entity permissions to also view/edit entities they have created.
*
* This must persist even if we later remove their create_entity permissions.
*
* The only clean way is to push each row through the sieve and push in view/edit permissions
* onto the users permissions array on a per-row basis.
*
* @param array $requested_actions - array of requested actions for menu
* @param stdClass $rows - requested $rows for datatable
* @param Class::class - need so we can harvest entity string
* @return stdClass
*/
public function processActionsForDatatable(array $requested_actions, $rows, $entity)
{
$rows->map(function ($row) use ($requested_actions, $entity){
$row->actions = $this->createActionCollection($requested_actions, $row, $entity);
return $row;
});
return $rows;
}
/**
* Method which allows the generation of a collection of action links
* for use when populating a Button.
*
* @param array $requested_actions - array of requested actions for menu
* @param Class::class - need so we can harvest entity string
* @return stdClass
*/
public function processActionsForButton(array $requested_actions, $entity)
{
$rows = $this->filterActions($requested_actions, auth()->user()->permissions(), auth()->user()->isAdmin());
$updated_actions = $rows->map(function($row) use ($entity){
$row['url'] = route($row['route'], [$row['key'] => $this->encodePrimaryKey($entity->id)]);
return $row;
});
return $updated_actions;
}
/**
* Builds the actions for a single row of a datatable
*
* @param array $requested_actions - array of requested actions for menu
* @param stdClass $row - single $row for datatable
* @param Class::class - need so we can harvest entity string
* @return Collection
*/
private function createActionCollection($requested_actions, $row, $entity) : Collection
{
$permissions = auth()->user()->permissions();
if(auth()->user()->owns($row))
array_push($permissions, 'view_' . strtolower(class_basename($entity)), 'edit_' .strtolower(class_basename($entity)));
$updated_actions = $this->filterActions($requested_actions, $permissions, auth()->user()->isAdmin())->map(function ($action) use($row){
$action['url'] = route($action['route'], [$action['key'] => $this->encodePrimaryKey($row->id)]);
return $action;
});
return $updated_actions;
}
/**
* Filters the main actions collection down to the requested
* actions for this menu
*
* @param array $actions Array of actions requested
* @param array $permissions Array of user permissions
* @param bool $isAdmin Boolean isAdmin
* @return Collection collection of filtered actions available to the user
*/
public function filterActions(array $actions, array $permissions, bool $is_admin) :Collection
{
return $this->checkPermissions($this->actions()->whereIn('action', $actions), $permissions, $is_admin);
}
/**
* Checks the user permissions against the collection and returns
* a Collection of available actions.
*
* @param Collection $actions collection of possible actions
* @param bool $isAdmin boolean defining if user is an administrator
* @return Collection collection of filtered actions
*
*/
private function checkPermissions(Collection $actions, array $permissions, bool $is_admin) :Collection
{
if($is_admin === TRUE)
return $actions;
return $actions->whereIn('permission', $permissions);
}
}

View File

@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use App\DataMapper\ClientSettings;
use App\Datatables\ClientDatatable;
use App\Datatables\MakesActionMenu;
use App\Factory\ClientFactory;
use App\Http\Requests\Client\CreateClientRequest;
@ -43,23 +42,15 @@ class ClientController extends Controller
*/
protected $clientRepo;
/**
* @var ClientDatatable
*/
protected $clientDatatable;
/**
* ClientController constructor.
* @param ClientRepository $clientRepo
* @param ClientDatatable $clientDatatable
*/
public function __construct(ClientRepository $clientRepo, ClientDatatable $clientDatatable)
public function __construct(ClientRepository $clientRepo)
{
$this->clientRepo = $clientRepo;
$this->clientDatatable = $clientDatatable;
}
/**
@ -67,16 +58,7 @@ class ClientController extends Controller
*/
public function index()
{
if(request('page'))
return $this->clientDatatable->query(request(), $this->getCurrentCompanyId());
$data = [
'datatable' => $this->clientDatatable->buildOptions(),
'listaction' => $this->clientDatatable->listActions()
];
return view('client.vue_list', $data);
return response()->json(Client::all());
}
/**
@ -88,29 +70,16 @@ class ClientController extends Controller
public function show(ShowClientRequest $request, Client $client)
{
$requested_view_statement_actions = [
'create_invoice_client_id',
'create_task_client_id',
'create_quote_client_id',
'create_recurring_invoice_client_id',
'create_payment_client_id',
'create_expense_client_id'
];
$data = [
'client' => $client,
'company' => auth()->user()->company(),
'meta' => collect([
'google_maps_api_key' => config('ninja.google_maps_api_key'),
'edit_client_permission' => auth()->user()->can('edit', $client),
'edit_client_route' => $this->processActionsForButton(['edit_client_client_id'], $client),
'view_statement_permission' => auth()->user()->can('view', $client),
'view_statement_route' => $this->processActionsForButton(['view_statement_client_id'], $client),
'view_statement_actions' => $this->processActionsForButton($requested_view_statement_actions, $client)
'google_maps_api_key' => config('ninja.google_maps_api_key')
])
];
return view('client.show', $data);
return response()->json($data);
}
@ -126,13 +95,12 @@ class ClientController extends Controller
$data = [
'client' => $client,
'settings' => collect(ClientSettings::buildClientSettings(auth()->user()->company()->settings_object, $client->client_settings_object)),
'pills' => $this->makeEntityTabMenu(Client::class),
'hashed_id' => $this->encodePrimarykey($client->id),
'company' => auth()->user()->company(),
'sizes' => Size::all(),
];
return view('client.edit', $data);
return response()->json($data);
}
@ -167,7 +135,7 @@ class ClientController extends Controller
'countries' => Country::all()
];
return view('client.create', $data);
return response()->json($data);
}
/**

View File

@ -29,7 +29,6 @@
"laravel/tinker": "^1.0",
"nwidart/laravel-modules": "^4.0",
"predis/predis": "^1.1",
"spatie/laravel-html": "^2.19",
"webpatser/laravel-countries": "dev-master#75992ad",
"wildbit/postmark-php": "^2.6"
},
@ -49,8 +48,7 @@
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Modules\\": "Modules/"
"App\\": "app/"
},
"files": [
"app/Libraries/OFX.php",

5826
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -232,7 +232,6 @@ return [
* Dependency Facades
*/
'Html' => Spatie\Html\Facades\Html::class,
'Countries' => 'Webpatser\Countries\CountriesFacade',
],

View File

@ -1,188 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Module Namespace
|--------------------------------------------------------------------------
|
| Default module namespace.
|
*/
'namespace' => 'Modules',
/*
|--------------------------------------------------------------------------
| Module Stubs
|--------------------------------------------------------------------------
|
| Default module stubs.
|
*/
'stubs' => [
'enabled' => false,
'path' => base_path() . '/vendor/nwidart/laravel-modules/src/Commands/stubs',
'files' => [
'routes/web' => 'Routes/web.php',
'routes/api' => 'Routes/api.php',
'views/index' => 'Resources/views/index.blade.php',
'views/master' => 'Resources/views/layouts/master.blade.php',
'scaffold/config' => 'Config/config.php',
'composer' => 'composer.json',
'assets/js/app' => 'Resources/assets/js/app.js',
'assets/sass/app' => 'Resources/assets/sass/app.scss',
'webpack' => 'webpack.mix.js',
'package' => 'package.json',
],
'replacements' => [
'routes/web' => ['LOWER_NAME', 'STUDLY_NAME'],
'routes/api' => ['LOWER_NAME'],
'webpack' => ['LOWER_NAME'],
'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE'],
'views/index' => ['LOWER_NAME'],
'views/master' => ['LOWER_NAME', 'STUDLY_NAME'],
'scaffold/config' => ['STUDLY_NAME'],
'composer' => [
'LOWER_NAME',
'STUDLY_NAME',
'VENDOR',
'AUTHOR_NAME',
'AUTHOR_EMAIL',
'MODULE_NAMESPACE',
],
],
'gitkeep' => true,
],
'paths' => [
/*
|--------------------------------------------------------------------------
| Modules path
|--------------------------------------------------------------------------
|
| This path used for save the generated module. This path also will be added
| automatically to list of scanned folders.
|
*/
'modules' => base_path('Modules'),
/*
|--------------------------------------------------------------------------
| Modules assets path
|--------------------------------------------------------------------------
|
| Here you may update the modules assets path.
|
*/
'assets' => public_path('modules'),
/*
|--------------------------------------------------------------------------
| The migrations path
|--------------------------------------------------------------------------
|
| Where you run 'module:publish-migration' command, where do you publish the
| the migration files?
|
*/
'migration' => base_path('database/migrations'),
/*
|--------------------------------------------------------------------------
| Generator path
|--------------------------------------------------------------------------
| Customise the paths where the folders will be generated.
| Set the generate key to false to not generate that folder
*/
'generator' => [
'config' => ['path' => 'Config', 'generate' => true],
'command' => ['path' => 'Console', 'generate' => true],
'migration' => ['path' => 'Database/Migrations', 'generate' => true],
'seeder' => ['path' => 'Database/Seeders', 'generate' => true],
'factory' => ['path' => 'Database/factories', 'generate' => true],
'model' => ['path' => 'Entities', 'generate' => true],
'controller' => ['path' => 'Http/Controllers', 'generate' => true],
'filter' => ['path' => 'Http/Middleware', 'generate' => true],
'request' => ['path' => 'Http/Requests', 'generate' => true],
'provider' => ['path' => 'Providers', 'generate' => true],
'assets' => ['path' => 'Resources/assets', 'generate' => true],
'lang' => ['path' => 'Resources/lang', 'generate' => true],
'views' => ['path' => 'Resources/views', 'generate' => true],
'test' => ['path' => 'Tests', 'generate' => true],
'repository' => ['path' => 'Repositories', 'generate' => false],
'event' => ['path' => 'Events', 'generate' => false],
'listener' => ['path' => 'Listeners', 'generate' => false],
'policies' => ['path' => 'Policies', 'generate' => false],
'rules' => ['path' => 'Rules', 'generate' => false],
'jobs' => ['path' => 'Jobs', 'generate' => false],
'emails' => ['path' => 'Emails', 'generate' => false],
'notifications' => ['path' => 'Notifications', 'generate' => false],
'resource' => ['path' => 'Transformers', 'generate' => false],
],
],
/*
|--------------------------------------------------------------------------
| Scan Path
|--------------------------------------------------------------------------
|
| Here you define which folder will be scanned. By default will scan vendor
| directory. This is useful if you host the package in packagist website.
|
*/
'scan' => [
'enabled' => false,
'paths' => [
base_path('vendor/*/*'),
],
],
/*
|--------------------------------------------------------------------------
| Composer File Template
|--------------------------------------------------------------------------
|
| Here is the config for composer.json file, generated by this package
|
*/
'composer' => [
'vendor' => 'nwidart',
'author' => [
'name' => 'Nicolas Widart',
'email' => 'n.widart@gmail.com',
],
],
/*
|--------------------------------------------------------------------------
| Caching
|--------------------------------------------------------------------------
|
| Here is the config for setting up caching feature.
|
*/
'cache' => [
'enabled' => true,
'key' => 'laravel-modules',
'lifetime' => 60,
],
/*
|--------------------------------------------------------------------------
| Choose what laravel-modules will register as custom namespaces.
| Setting one to false will require you to register that part
| in your own Service Provider class.
|--------------------------------------------------------------------------
*/
'register' => [
'translations' => true,
/**
* load files on boot or register method
*
* Note: boot not compatible with asgardcms
*
* @example boot|register
*/
'files' => 'register',
],
];

View File

@ -4,7 +4,7 @@ return [
'web_url' => 'https://www.invoiceninja.com',
'app_name' => env('APP_NAME'),
'site_url' => env('APP_URL', 'https://app-v5.invoiceninja.com'),
'site_url' => env('APP_URL', 'https://v2.invoiceninja.com'),
'app_domain' => env('APP_DOMAIN', 'invoiceninja.com'),
'app_version' => '0.1',
'terms_version' => '1.0.1',