├── .gitignore ├── composer.json ├── config └── permission.php ├── database ├── migrations │ └── create_permission_tables.php.stub └── seeds │ └── RolesAndPermissionsSeeder.php.stub ├── dist ├── css │ └── tool.css ├── js │ └── tool.js └── mix-manifest.json ├── package.json ├── readme.md ├── resources ├── js │ ├── components │ │ ├── DetailField.vue │ │ ├── FormField.vue │ │ └── IndexField.vue │ └── tool.js ├── sass │ └── tool.scss └── views │ └── navigation.blade.php ├── routes └── api.php ├── src ├── Checkboxes.php ├── Http │ └── Middleware │ │ └── Authorize.php ├── Nova │ ├── Permission.php │ ├── ResourceForUser.php │ └── Role.php ├── NovaPermissions.php ├── Policies │ └── Policy.php ├── Role.php └── ToolServiceProvider.php ├── webpack.mix.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /node_modules 4 | package-lock.json 5 | composer.phar 6 | composer.lock 7 | phpunit.xml 8 | .phpunit.result.cache 9 | .DS_Store 10 | Thumbs.db 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eminiarts/nova-permissions", 3 | "description": "Laravel Nova Grouped Permissions.", 4 | "keywords": [ 5 | "laravel", 6 | "nova" 7 | ], 8 | "license": "MIT", 9 | "require": { 10 | "php": ">=7.2.0", 11 | "spatie/laravel-permission": "3.*" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Eminiarts\\NovaPermissions\\": "src/" 16 | } 17 | }, 18 | "extra": { 19 | "laravel": { 20 | "providers": [ 21 | "Eminiarts\\NovaPermissions\\ToolServiceProvider" 22 | ] 23 | } 24 | }, 25 | "config": { 26 | "sort-packages": true 27 | }, 28 | "minimum-stability": "dev", 29 | "prefer-stable": true 30 | } 31 | -------------------------------------------------------------------------------- /config/permission.php: -------------------------------------------------------------------------------- 1 | [ 5 | /* 6 | * When using the "HasPermissions" trait from this package, we need to know which 7 | * Eloquent model should be used to retrieve your permissions. Of course, it 8 | * is often just the "Permission" model but you may use whatever you like. 9 | * 10 | * The model you want to use as a Permission model needs to implement the 11 | * `Spatie\Permission\Contracts\Permission` contract. 12 | */ 13 | 'permission' => Spatie\Permission\Models\Permission::class, 14 | /* 15 | * When using the "HasRoles" trait from this package, we need to know which 16 | * Eloquent model should be used to retrieve your roles. Of course, it 17 | * is often just the "Role" model but you may use whatever you like. 18 | * 19 | * The model you want to use as a Role model needs to implement the 20 | * `Spatie\Permission\Contracts\Role` contract. 21 | */ 22 | 'role' => Eminiarts\NovaPermissions\Role::class, 23 | ], 24 | 'table_names' => [ 25 | /* 26 | * When using the "HasRoles" trait from this package, we need to know which 27 | * table should be used to retrieve your roles. We have chosen a basic 28 | * default value but you may easily change it to any table you like. 29 | */ 30 | 'roles' => 'roles', 31 | /* 32 | * When using the "HasPermissions" trait from this package, we need to know which 33 | * table should be used to retrieve your permissions. We have chosen a basic 34 | * default value but you may easily change it to any table you like. 35 | */ 36 | 'permissions' => 'permissions', 37 | /* 38 | * When using the "HasPermissions" trait from this package, we need to know which 39 | * table should be used to retrieve your models permissions. We have chosen a 40 | * basic default value but you may easily change it to any table you like. 41 | */ 42 | 'model_has_permissions' => 'model_has_permissions', 43 | /* 44 | * When using the "HasRoles" trait from this package, we need to know which 45 | * table should be used to retrieve your models roles. We have chosen a 46 | * basic default value but you may easily change it to any table you like. 47 | */ 48 | 'model_has_roles' => 'model_has_roles', 49 | /* 50 | * When using the "HasRoles" trait from this package, we need to know which 51 | * table should be used to retrieve your roles permissions. We have chosen a 52 | * basic default value but you may easily change it to any table you like. 53 | */ 54 | 'role_has_permissions' => 'role_has_permissions', 55 | ], 56 | 'column_names' => [ 57 | /* 58 | * Change this if you want to name the related model primary key other than 59 | * `model_id`. 60 | * 61 | * For example, this would be nice if your primary keys are all UUIDs. In 62 | * that case, name this `model_uuid`. 63 | */ 64 | 'model_morph_key' => 'model_id', 65 | ], 66 | /* 67 | * When set to true, the required permission/role names are added to the exception 68 | * message. This could be considered an information leak in some contexts, so 69 | * the default setting is false here for optimum safety. 70 | */ 71 | 'display_permission_in_exception' => false, 72 | 'cache' => [ 73 | /* 74 | * By default all permissions are cached for 24 hours to speed up performance. 75 | * When permissions or roles are updated the cache is flushed automatically. 76 | */ 77 | 'expiration_time' => \DateInterval::createFromDateString('24 hours'), 78 | /* 79 | * The key to use when tagging and prefixing entries in the cache. 80 | */ 81 | 'key' => 'spatie.permission.cache', 82 | /* 83 | * When checking for a permission against a model by passing a Permission 84 | * instance to the check, this key determines what attribute on the 85 | * Permissions model is used to cache against. 86 | * 87 | * Ideally, this should match your preferred way of checking permissions, eg: 88 | * `$user->can('view-posts')` would be 'name'. 89 | */ 90 | 'model_key' => 'name', 91 | /* 92 | * You may optionally indicate a specific cache driver to use for permission and 93 | * role caching using any of the `store` drivers listed in the cache.php config 94 | * file. Using 'default' here means to use the `default` set in cache.php. 95 | */ 96 | 'store' => 'default', 97 | ], 98 | ]; 99 | -------------------------------------------------------------------------------- /database/migrations/create_permission_tables.php.stub: -------------------------------------------------------------------------------- 1 | increments('id'); 21 | $table->string('name'); 22 | $table->string('group'); 23 | $table->string('guard_name'); 24 | $table->timestamps(); 25 | }); 26 | 27 | Schema::create($tableNames['roles'], function (Blueprint $table) { 28 | $table->increments('id'); 29 | $table->string('name'); 30 | $table->string('guard_name'); 31 | $table->timestamps(); 32 | }); 33 | 34 | Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames) { 35 | $table->unsignedInteger('permission_id'); 36 | 37 | $table->string('model_type'); 38 | $table->unsignedBigInteger($columnNames['model_morph_key']); 39 | $table->index([$columnNames['model_morph_key'], 'model_type', ]); 40 | 41 | $table->foreign('permission_id') 42 | ->references('id') 43 | ->on($tableNames['permissions']) 44 | ->onDelete('cascade'); 45 | 46 | $table->primary(['permission_id', $columnNames['model_morph_key'], 'model_type'], 47 | 'model_has_permissions_permission_model_type_primary'); 48 | }); 49 | 50 | Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames) { 51 | $table->unsignedInteger('role_id'); 52 | 53 | $table->string('model_type'); 54 | $table->unsignedBigInteger($columnNames['model_morph_key']); 55 | $table->index([$columnNames['model_morph_key'], 'model_type', ]); 56 | 57 | $table->foreign('role_id') 58 | ->references('id') 59 | ->on($tableNames['roles']) 60 | ->onDelete('cascade'); 61 | 62 | $table->primary(['role_id', $columnNames['model_morph_key'], 'model_type'], 63 | 'model_has_roles_role_model_type_primary'); 64 | }); 65 | 66 | Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) { 67 | $table->unsignedInteger('permission_id'); 68 | $table->unsignedInteger('role_id'); 69 | 70 | $table->foreign('permission_id') 71 | ->references('id') 72 | ->on($tableNames['permissions']) 73 | ->onDelete('cascade'); 74 | 75 | $table->foreign('role_id') 76 | ->references('id') 77 | ->on($tableNames['roles']) 78 | ->onDelete('cascade'); 79 | 80 | $table->primary(['permission_id', 'role_id']); 81 | }); 82 | 83 | app('cache') 84 | ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) 85 | ->forget(config('permission.cache.key')); 86 | } 87 | 88 | /** 89 | * Reverse the migrations. 90 | * 91 | * @return void 92 | */ 93 | public function down() 94 | { 95 | $tableNames = config('permission.table_names'); 96 | 97 | Schema::drop($tableNames['role_has_permissions']); 98 | Schema::drop($tableNames['model_has_roles']); 99 | Schema::drop($tableNames['model_has_permissions']); 100 | Schema::drop($tableNames['roles']); 101 | Schema::drop($tableNames['permissions']); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /database/seeds/RolesAndPermissionsSeeder.php.stub: -------------------------------------------------------------------------------- 1 | forgetCachedPermissions(); 18 | 19 | $collection = collect([ 20 | 'users', 21 | 'roles', 22 | 'permissions', 23 | // 'teams', 24 | // ... // List all your Models you want to have Permissions for. 25 | ]); 26 | 27 | $collection->each(function ($item, $key) { 28 | // create permissions for each collection item 29 | Permission::create(['group' => $item, 'name' => 'view ' . $item]); 30 | Permission::create(['group' => $item, 'name' => 'view own ' . $item]); 31 | Permission::create(['group' => $item, 'name' => 'manage ' . $item]); 32 | Permission::create(['group' => $item, 'name' => 'manage own ' . $item]); 33 | Permission::create(['group' => $item, 'name' => 'restore ' . $item]); 34 | Permission::create(['group' => $item, 'name' => 'forceDelete ' . $item]); 35 | }); 36 | 37 | // Create a Super-Admin Role and assign all Permissions 38 | $role = Role::create(['name' => 'super-admin']); 39 | $role->givePermissionTo(Permission::all()); 40 | 41 | // Give User Super-Admin Role 42 | // $user = App\User::whereEmail('your@email.com')->first(); // Change this to your email. 43 | // $user->assignRole('super-admin'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dist/css/tool.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eminiarts/nova-permissions/8390c53e46f3305dcada099e464c9e1758f6518b/dist/css/tool.css -------------------------------------------------------------------------------- /dist/js/tool.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=3)}([function(e,t){e.exports=function(e,t,r,n,o,i){var u,s=e=e||{},a=typeof e.default;"object"!==a&&"function"!==a||(u=e,s=e.default);var c,f="function"==typeof s?s.options:s;if(t&&(f.render=t.render,f.staticRenderFns=t.staticRenderFns,f._compiled=!0),r&&(f.functional=!0),o&&(f._scopeId=o),i?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(i)},f._ssrRegister=c):n&&(c=n),c){var l=f.functional,p=l?f.render:f.beforeCreate;l?(f._injectStyles=c,f.render=function(e,t){return c.call(t),p(e,t)}):f.beforeCreate=p?[].concat(p,c):[c]}return{esModule:u,exports:s,options:f}}},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var r=function(e,t){var r=e[1]||"",n=e[3];if(!n)return r;if(t&&"function"==typeof btoa){var o=(u=n,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(u))))+" */"),i=n.sources.map(function(e){return"/*# sourceURL="+n.sourceRoot+e+" */"});return[r].concat(i).concat([o]).join("\n")}var u;return[r].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+r+"}":r}).join("")},t.i=function(e,r){"string"==typeof e&&(e=[[null,e,""]]);for(var n={},o=0;or.parts.length&&(n.parts.length=r.parts.length)}else{var u=[];for(o=0;o=200&&e<300}};a.headers={common:{Accept:"application/json, text/plain, */*"}},n.forEach(["delete","get","head"],function(e){a.headers[e]={}}),n.forEach(["post","put","patch"],function(e){a.headers[e]=n.merge(i)}),e.exports=a}).call(t,r(75))},function(e,t,r){"use strict";t.__esModule=!0;var n,o=r(113),i=(n=o)&&n.__esModule?n:{default:n};t.default=function(e,t,r){return t in e?(0,i.default)(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,r){var n=r(9),o=r(1).document,i=n(o)&&n(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports=!0},function(e,t,r){"use strict";var n=r(14);e.exports.f=function(e){return new function(e){var t,r;this.promise=new e(function(e,n){if(void 0!==t||void 0!==r)throw TypeError("Bad Promise constructor");t=e,r=n}),this.resolve=n(t),this.reject=n(r)}(e)}},function(e,t,r){var n=r(11).f,o=r(17),i=r(2)("toStringTag");e.exports=function(e,t,r){e&&!o(e=r?e:e.prototype,i)&&n(e,i,{configurable:!0,value:t})}},function(e,t,r){var n=r(60)("keys"),o=r(65);e.exports=function(e){return n[e]||(n[e]=o(e))}},function(e,t){var r=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:r)(e)}},function(e,t,r){var n=r(54),o=r(27);e.exports=function(e){return n(o(e))}},function(e,t,r){var n=r(12).Symbol;e.exports=n},function(e,t,r){var n=r(170),o=r(189);e.exports=function(e,t){var r=o(e,t);return n(r)?r:void 0}},function(e,t){e.exports=function(e,t){return e===t||e!=e&&t!=t}},function(e,t){e.exports=function(e){return e}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=["1/2","1/3","2/3","1/4","3/4","1/5","2/5","3/5","4/5","1/6","5/6"]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(154);Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i(n).default}}),Object.defineProperty(t,"Form",{enumerable:!0,get:function(){return i(n).default}});var o=r(66);function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"Errors",{enumerable:!0,get:function(){return i(o).default}})},function(e,t,r){"use strict";(function(t){var n=r(0),o=r(101),i=r(104),u=r(110),s=r(108),a=r(45),c="undefined"!=typeof window&&window.btoa&&window.btoa.bind(window)||r(103);e.exports=function(e){return new Promise(function(f,l){var p=e.data,h=e.headers;n.isFormData(p)&&delete h["Content-Type"];var d=new XMLHttpRequest,v="onreadystatechange",y=!1;if("test"===t.env.NODE_ENV||"undefined"==typeof window||!window.XDomainRequest||"withCredentials"in d||s(e.url)||(d=new window.XDomainRequest,v="onload",y=!0,d.onprogress=function(){},d.ontimeout=function(){}),e.auth){var g=e.auth.username||"",m=e.auth.password||"";h.Authorization="Basic "+c(g+":"+m)}if(d.open(e.method.toUpperCase(),i(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d[v]=function(){if(d&&(4===d.readyState||y)&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var t="getAllResponseHeaders"in d?u(d.getAllResponseHeaders()):null,r={data:e.responseType&&"text"!==e.responseType?d.response:d.responseText,status:1223===d.status?204:d.status,statusText:1223===d.status?"No Content":d.statusText,headers:t,config:e,request:d};o(f,l,r),d=null}},d.onerror=function(){l(a("Network Error",e,null,d)),d=null},d.ontimeout=function(){l(a("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",d)),d=null},n.isStandardBrowserEnv()){var x=r(106),b=(e.withCredentials||s(e.url))&&e.xsrfCookieName?x.read(e.xsrfCookieName):void 0;b&&(h[e.xsrfHeaderName]=b)}if("setRequestHeader"in d&&n.forEach(h,function(e,t){void 0===p&&"content-type"===t.toLowerCase()?delete h[t]:d.setRequestHeader(t,e)}),e.withCredentials&&(d.withCredentials=!0),e.responseType)try{d.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){d&&(d.abort(),l(e),d=null)}),void 0===p&&(p=null),d.send(p)})}}).call(t,r(75))},function(e,t,r){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,r){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,r){"use strict";var n=r(100);e.exports=function(e,t,r,o,i){var u=new Error(e);return n(u,t,r,o,i)}},function(e,t,r){"use strict";e.exports=function(e,t){return function(){for(var r=new Array(arguments.length),n=0;nr;)t.push(arguments[r++]);return g[++y]=function(){s("function"==typeof e?e:Function(e),t)},n(y),y},h=function(e){delete g[e]},"process"==r(15)(l)?n=function(e){l.nextTick(u(m,e,1))}:v&&v.now?n=function(e){v.now(u(m,e,1))}:d?(i=(o=new d).port2,o.port1.onmessage=x,n=u(i.postMessage,i,1)):f.addEventListener&&"function"==typeof postMessage&&!f.importScripts?(n=function(e){f.postMessage(e+"","*")},f.addEventListener("message",x,!1)):n="onreadystatechange"in c("script")?function(e){a.appendChild(c("script")).onreadystatechange=function(){a.removeChild(this),m.call(e)}}:function(e){setTimeout(u(m,e,1),0)}),e.exports={set:p,clear:h}},function(e,t,r){var n=r(34),o=Math.min;e.exports=function(e){return e>0?o(n(e),9007199254740991):0}},function(e,t,r){var n=r(27);e.exports=function(e){return Object(n(e))}},function(e,t){var r=0,n=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++r+n).toString(36))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.record(t)}return n(e,[{key:"all",value:function(){return this.errors}},{key:"has",value:function(e){var t=this.errors.hasOwnProperty(e);t||(t=Object.keys(this.errors).filter(function(t){return t.startsWith(e+".")||t.startsWith(e+"[")}).length>0);return t}},{key:"first",value:function(e){return this.get(e)[0]}},{key:"get",value:function(e){return this.errors[e]||[]}},{key:"any",value:function(){return Object.keys(this.errors).length>0}},{key:"record",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.errors=e}},{key:"clear",value:function(e){if(e){var t=Object.assign({},this.errors);Object.keys(t).filter(function(t){return t===e||t.startsWith(e+".")||t.startsWith(e+"[")}).forEach(function(e){return delete t[e]}),this.errors=t}else this.errors={}}}]),e}();t.default=o},function(e,t,r){var n=r(177),o=r(229),i=r(13),u=r(230),s=r(70),a=r(231),c=Object.prototype.hasOwnProperty;e.exports=function(e,t){var r=i(e),f=!r&&o(e),l=!r&&!f&&u(e),p=!r&&!f&&!l&&a(e),h=r||f||l||p,d=h?n(e.length,String):[],v=d.length;for(var y in e)!t&&!c.call(e,y)||h&&("length"==y||l&&("offset"==y||"parent"==y)||p&&("buffer"==y||"byteLength"==y||"byteOffset"==y)||s(y,v))||d.push(y);return d}},function(e,t,r){(function(t){var r="object"==typeof t&&t&&t.Object===Object&&t;e.exports=r}).call(t,r(241))},function(e,t){var r=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");e.exports=function(e){return r.test(e)}},function(e,t){var r=9007199254740991,n=/^(?:0|[1-9]\d*)$/;e.exports=function(e,t){var o=typeof e;return!!(t=null==t?r:t)&&("number"==o||"symbol"!=o&&n.test(e))&&e>-1&&e%1==0&&e-1&&e%1==0&&e<=r}},function(e,t,r){var n=r(178);e.exports=function(e){return null==e?"":n(e)}},function(e,t){var r,n,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function u(){throw new Error("clearTimeout has not been defined")}function s(e){if(r===setTimeout)return setTimeout(e,0);if((r===i||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(t){try{return r.call(null,e,0)}catch(t){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:i}catch(e){r=i}try{n="function"==typeof clearTimeout?clearTimeout:u}catch(e){n=u}}();var a,c=[],f=!1,l=-1;function p(){f&&a&&(f=!1,a.length?c=a.concat(c):l=-1,c.length&&h())}function h(){if(!f){var e=s(p);f=!0;for(var t=c.length;t;){for(a=c,c=[];++l1)for(var r=1;r1&&void 0!==arguments[1]?arguments[1]:null;return this.viaManyToMany?this.detachResources(e):Nova.request({url:"/nova-api/"+this.resourceName,method:"delete",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(r||function(){t.deleteModalOpen=!1,t.getResources()})},deleteSelectedResources:function(){this.deleteResources(this.selectedResources)},deleteAllMatchingResources:function(){var e=this;return this.viaManyToMany?this.detachAllMatchingResources():Nova.request({url:this.deleteAllMatchingResourcesEndpoint,method:"delete",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.deleteModalOpen=!1,e.getResources()})},detachResources:function(e){var t=this;return Nova.request({url:"/nova-api/"+this.resourceName+"/detach",method:"delete",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(function(){t.deleteModalOpen=!1,t.getResources()})},detachAllMatchingResources:function(){var e=this;return Nova.request({url:"/nova-api/"+this.resourceName+"/detach",method:"delete",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.deleteModalOpen=!1,e.getResources()})},forceDeleteResources:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return Nova.request({url:"/nova-api/"+this.resourceName+"/force",method:"delete",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(r||function(){t.deleteModalOpen=!1,t.getResources()})},forceDeleteSelectedResources:function(){this.forceDeleteResources(this.selectedResources)},forceDeleteAllMatchingResources:function(){var e=this;return Nova.request({url:this.forceDeleteSelectedResourcesEndpoint,method:"delete",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.deleteModalOpen=!1,e.getResources()})},restoreResources:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return Nova.request({url:"/nova-api/"+this.resourceName+"/restore",method:"put",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(r||function(){t.restoreModalOpen=!1,t.getResources()})},restoreSelectedResources:function(){this.restoreResources(this.selectedResources)},restoreAllMatchingResources:function(){var e=this;return Nova.request({url:this.restoreAllMatchingResourcesEndpoint,method:"put",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.restoreModalOpen=!1,e.getResources()})}},computed:{deleteAllMatchingResourcesEndpoint:function(){return this.lens?"/nova-api/"+this.resourceName+"/lens/"+this.lens:"/nova-api/"+this.resourceName},forceDeleteSelectedResourcesEndpoint:function(){return this.lens?"/nova-api/"+this.resourceName+"/lens/"+this.lens+"/force":"/nova-api/"+this.resourceName+"/force"},restoreAllMatchingResourcesEndpoint:function(){return this.lens?"/nova-api/"+this.resourceName+"/lens/"+this.lens+"/restore":"/nova-api/"+this.resourceName+"/restore"},queryString:function(){return{search:this.currentSearch,filters:this.encodedFilters,trashed:this.currentTrashed,viaResource:this.viaResource,viaResourceId:this.viaResourceId,viaRelationship:this.viaRelationship}}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=u(r(50)),o=u(r(26)),i=u(r(49));u(r(226)),u(r(228));function u(e){return e&&e.__esModule?e:{default:e}}t.default={methods:{clearSelectedFilters:function(){var e=(0,i.default)(n.default.mark(function e(t){var r;return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!t){e.next=5;break}return e.next=3,this.$store.dispatch(this.resourceName+"/resetFilterState",{resourceName:this.resourceName,lens:t});case 3:e.next=7;break;case 5:return e.next=7,this.$store.dispatch(this.resourceName+"/resetFilterState",{resourceName:this.resourceName});case 7:this.updateQueryString((r={},(0,o.default)(r,this.pageParameter,1),(0,o.default)(r,this.filterParameter,""),r));case 8:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}(),filterChanged:function(){var e;this.updateQueryString((e={},(0,o.default)(e,this.pageParameter,1),(0,o.default)(e,this.filterParameter,this.$store.getters[this.resourceName+"/currentEncodedFilters"]),e))},initializeFilters:function(){var e=(0,i.default)(n.default.mark(function e(t){return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return this.$store.commit(this.resourceName+"/clearFilters"),e.next=3,this.$store.dispatch(this.resourceName+"/fetchFilters",{resourceName:this.resourceName,lens:t});case 3:return e.next=5,this.initializeState(t);case 5:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}(),initializeState:function(){var e=(0,i.default)(n.default.mark(function e(t){return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.initialEncodedFilters){e.next=5;break}return e.next=3,this.$store.dispatch(this.resourceName+"/initializeCurrentFilterValuesFromQueryString",this.initialEncodedFilters);case 3:e.next=7;break;case 5:return e.next=7,this.$store.dispatch(this.resourceName+"/resetFilterState",{resourceName:this.resourceName,lens:t});case 7:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}()},computed:{filterParameter:function(){return this.resourceName+"_filter"}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{resourceName:{},field:{}},data:function(){return{value:""}},mounted:function(){var e=this;this.setInitialValue(),this.field.fill=this.fill,Nova.$on(this.field.attribute+"-value",function(t){e.value=t})},destroyed:function(){Nova.$off(this.field.attribute+"-value")},methods:{setInitialValue:function(){this.value=void 0!==this.field.value&&null!==this.field.value?this.field.value:""},fill:function(e){e.append(this.field.attribute,String(this.value))},handleChange:function(e){this.value=e}},computed:{isReadonly:function(){return this.field.readonly||_.get(this.field,"extraAttributes.readonly")}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(41);t.default={props:{errors:{default:function(){return new n.Errors}}},data:function(){return{errorClass:"border-danger"}},computed:{errorClasses:function(){return this.hasError?[this.errorClass]:[]},fieldAttribute:function(){return this.field.attribute},hasError:function(){return this.errors.has(this.fieldAttribute)},firstError:function(){if(this.hasError)return this.errors.first(this.fieldAttribute)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=u(r(50)),o=u(r(49)),i=u(r(40));function u(e){return e&&e.__esModule?e:{default:e}}t.default={props:{loadCards:{type:Boolean,default:!0}},data:function(){return{cards:[]}},created:function(){this.fetchCards()},watch:{cardsEndpoint:function(){this.fetchCards()}},methods:{fetchCards:function(){var e=(0,o.default)(n.default.mark(function e(){var t,r;return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.loadCards){e.next=6;break}return e.next=3,Nova.request().get(this.cardsEndpoint,{params:this.extraCardParams});case 3:t=e.sent,r=t.data,this.cards=r;case 6:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},computed:{shouldShowCards:function(){return this.cards.length>0},smallCards:function(){return _.filter(this.cards,function(e){return-1!==i.default.indexOf(e.width)})},largeCards:function(){return _.filter(this.cards,function(e){return"full"==e.width})},extraCardParams:function(){return null}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={methods:{toAppTimezone:function(e){return e?moment.tz(e,this.userTimezone).clone().tz(Nova.config.timezone).format("YYYY-MM-DD HH:mm:ss"):e},fromAppTimezone:function(e){return e?moment.tz(e,Nova.config.timezone).clone().tz(this.userTimezone).format("YYYY-MM-DD HH:mm:ss"):e},localizeDateTimeField:function(e){if(!e.value)return e.value;var t=moment.tz(e.value,Nova.config.timezone).clone().tz(this.userTimezone);return e.format?t.format(e.format):this.usesTwelveHourTime?t.format("YYYY-MM-DD h:mm:ss A"):t.format("YYYY-MM-DD HH:mm:ss")},localizeDateField:function(e){if(!e.value)return e.value;var t=moment.tz(e.value,Nova.config.timezone).clone().tz(this.userTimezone);return e.format?t.format(e.format):t.format("YYYY-MM-DD")}},computed:{userTimezone:function(){return Nova.config.userTimezone?Nova.config.userTimezone:moment.tz.guess()},usesTwelveHourTime:function(){return _.endsWith((new Date).toLocaleString(),"AM")||_.endsWith((new Date).toLocaleString(),"PM")}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(225),i=(n=o)&&n.__esModule?n:{default:n};t.default={methods:{updateQueryString:function(e){this.$router.push({query:(0,i.default)(e,this.$route.query)})}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={computed:{resourceInformation:function(){var e=this;return _.find(Nova.config.resources,function(t){return t.uriKey==e.resourceName})},viaResourceInformation:function(){var e=this;if(this.viaResource)return _.find(Nova.config.resources,function(t){return t.uriKey==e.viaResource})},authorizedToCreate:function(){return this.resourceInformation.authorizedToCreate}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(26),i=(n=o)&&n.__esModule?n:{default:n};t.default={methods:{selectPreviousPage:function(){this.updateQueryString((0,i.default)({},this.pageParameter,this.currentPage-1))},selectNextPage:function(){this.updateQueryString((0,i.default)({},this.pageParameter,this.currentPage+1))}},computed:{currentPage:function(){return parseInt(this.$route.query[this.pageParameter]||1)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(26),i=(n=o)&&n.__esModule?n:{default:n};t.default={data:function(){return{perPage:25}},methods:{initializePerPageFromQueryString:function(){this.perPage=this.currentPerPage},perPageChanged:function(){this.updateQueryString((0,i.default)({},this.perPageParameter,this.perPage))}},computed:{currentPerPage:function(){return this.$route.query[this.perPageParameter]||25}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(224),i=(n=o)&&n.__esModule?n:{default:n};t.default={data:function(){return{search:"",selectedResource:"",availableResources:[]}},methods:{selectResource:function(e){this.selectedResource=e},handleSearchCleared:function(){this.availableResources=[]},clearSelection:function(){this.selectedResource="",this.availableResources=[]},performSearch:function(e){var t=this;this.search=e;var r=e.trim();""!=r?this.debouncer(function(){t.selectedResource="",t.getAvailableResources(r)},500):this.clearSelection()},debouncer:(0,i.default)(function(e){return e()},500)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={data:function(){return{withTrashed:!1}},methods:{toggleWithTrashed:function(){this.withTrashed=!this.withTrashed},enableWithTrashed:function(){this.withTrashed=!0},disableWithTrashed:function(){this.withTrashed=!1}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return(0,i.default)(e)};var n,o=r(238),i=(n=o)&&n.__esModule?n:{default:n}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(48),i=(n=o)&&n.__esModule?n:{default:n};t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100;return i.default.all([e,new i.default(function(e){setTimeout(function(){return e()},t)})]).then(function(e){return e[0]})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return e>1||0==e?n.Inflector.pluralize(t):n.Inflector.singularize(t)};var n=r(47)},function(e,t,r){"use strict";var n={uncountableWords:["equipment","information","rice","money","species","series","fish","sheep","moose","deer","news"],pluralRules:[[new RegExp("(m)an$","gi"),"$1en"],[new RegExp("(pe)rson$","gi"),"$1ople"],[new RegExp("(child)$","gi"),"$1ren"],[new RegExp("^(ox)$","gi"),"$1en"],[new RegExp("(ax|test)is$","gi"),"$1es"],[new RegExp("(octop|vir)us$","gi"),"$1i"],[new RegExp("(alias|status)$","gi"),"$1es"],[new RegExp("(bu)s$","gi"),"$1ses"],[new RegExp("(buffal|tomat|potat)o$","gi"),"$1oes"],[new RegExp("([ti])um$","gi"),"$1a"],[new RegExp("sis$","gi"),"ses"],[new RegExp("(?:([^f])fe|([lr])f)$","gi"),"$1$2ves"],[new RegExp("(hive)$","gi"),"$1s"],[new RegExp("([^aeiouy]|qu)y$","gi"),"$1ies"],[new RegExp("(x|ch|ss|sh)$","gi"),"$1es"],[new RegExp("(matr|vert|ind)ix|ex$","gi"),"$1ices"],[new RegExp("([m|l])ouse$","gi"),"$1ice"],[new RegExp("(quiz)$","gi"),"$1zes"],[new RegExp("s$","gi"),"s"],[new RegExp("$","gi"),"s"]],singularRules:[[new RegExp("(m)en$","gi"),"$1an"],[new RegExp("(pe)ople$","gi"),"$1rson"],[new RegExp("(child)ren$","gi"),"$1"],[new RegExp("([ti])a$","gi"),"$1um"],[new RegExp("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$","gi"),"$1$2sis"],[new RegExp("(hive)s$","gi"),"$1"],[new RegExp("(tive)s$","gi"),"$1"],[new RegExp("(curve)s$","gi"),"$1"],[new RegExp("([lr])ves$","gi"),"$1f"],[new RegExp("([^fo])ves$","gi"),"$1fe"],[new RegExp("([^aeiouy]|qu)ies$","gi"),"$1y"],[new RegExp("(s)eries$","gi"),"$1eries"],[new RegExp("(m)ovies$","gi"),"$1ovie"],[new RegExp("(x|ch|ss|sh)es$","gi"),"$1"],[new RegExp("([m|l])ice$","gi"),"$1ouse"],[new RegExp("(bus)es$","gi"),"$1"],[new RegExp("(o)es$","gi"),"$1"],[new RegExp("(shoe)s$","gi"),"$1"],[new RegExp("(cris|ax|test)es$","gi"),"$1is"],[new RegExp("(octop|vir)i$","gi"),"$1us"],[new RegExp("(alias|status)es$","gi"),"$1"],[new RegExp("^(ox)en","gi"),"$1"],[new RegExp("(vert|ind)ices$","gi"),"$1ex"],[new RegExp("(matr)ices$","gi"),"$1ix"],[new RegExp("(quiz)zes$","gi"),"$1"],[new RegExp("s$","gi"),""]],nonTitlecasedWords:["and","or","nor","a","an","the","so","but","to","of","at","by","from","into","on","onto","off","out","in","over","with","for"],idSuffix:new RegExp("(_ids|_id)$","g"),underbar:new RegExp("_","g"),spaceOrUnderbar:new RegExp("[ _]","g"),uppercase:new RegExp("([A-Z])","g"),underbarPrefix:new RegExp("^_"),applyRules:function(e,t,r,n){if(n)e=n;else if(!(r.indexOf(e.toLowerCase())>-1))for(var o=0;o>8-s%1*8)){if((r=i.charCodeAt(s+=.75))>255)throw new o;t=t<<8|r}return u}},function(e,t,r){"use strict";var n=r(0);function o(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,r){if(!t)return e;var i;if(r)i=r(t);else if(n.isURLSearchParams(t))i=t.toString();else{var u=[];n.forEach(t,function(e,t){null!=e&&(n.isArray(e)?t+="[]":e=[e],n.forEach(e,function(e){n.isDate(e)?e=e.toISOString():n.isObject(e)&&(e=JSON.stringify(e)),u.push(o(t)+"="+o(e))}))}),i=u.join("&")}return i&&(e+=(-1===e.indexOf("?")?"?":"&")+i),e}},function(e,t,r){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,r){"use strict";var n=r(0);e.exports=n.isStandardBrowserEnv()?{write:function(e,t,r,o,i,u){var s=[];s.push(e+"="+encodeURIComponent(t)),n.isNumber(r)&&s.push("expires="+new Date(r).toGMTString()),n.isString(o)&&s.push("path="+o),n.isString(i)&&s.push("domain="+i),!0===u&&s.push("secure"),document.cookie=s.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,r){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,r){"use strict";var n=r(0);e.exports=n.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function o(e){var n=e;return t&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return e=o(window.location.href),function(t){var r=n.isString(t)?o(t):t;return r.protocol===e.protocol&&r.host===e.host}}():function(){return!0}},function(e,t,r){"use strict";var n=r(0);e.exports=function(e,t){n.forEach(e,function(r,n){n!==t&&n.toUpperCase()===t.toUpperCase()&&(e[t]=r,delete e[n])})}},function(e,t,r){"use strict";var n=r(0),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,r,i,u={};return e?(n.forEach(e.split("\n"),function(e){if(i=e.indexOf(":"),t=n.trim(e.substr(0,i)).toLowerCase(),r=n.trim(e.substr(i+1)),t){if(u[t]&&o.indexOf(t)>=0)return;u[t]="set-cookie"===t?(u[t]?u[t]:[]).concat([r]):u[t]?u[t]+", "+r:r}}),u):u}},function(e,t,r){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,r){e.exports={default:r(115),__esModule:!0}},function(e,t,r){e.exports={default:r(116),__esModule:!0}},function(e,t,r){"use strict";t.__esModule=!0;var n,o=r(112),i=(n=o)&&n.__esModule?n:{default:n};t.default=i.default||function(e){for(var t=1;tf;)if((s=a[f++])!=s)return!0}else for(;c>f;f++)if((e||f in a)&&a[f]===r)return e||f||0;return!e&&-1}}},function(e,t,r){var n=r(16),o=r(125),i=r(124),u=r(4),s=r(63),a=r(144),c={},f={};(t=e.exports=function(e,t,r,l,p){var h,d,v,y,g=p?function(){return e}:a(e),m=n(r,l,t?2:1),x=0;if("function"!=typeof g)throw TypeError(e+" is not iterable!");if(i(g)){for(h=s(e.length);h>x;x++)if((y=t?m(u(d=e[x])[0],d[1]):m(e[x]))===c||y===f)return y}else for(v=g.call(e);!(d=v.next()).done;)if((y=o(v,m,d.value,t))===c||y===f)return y}).BREAK=c,t.RETURN=f},function(e,t,r){e.exports=!r(5)&&!r(29)(function(){return 7!=Object.defineProperty(r(28)("div"),"a",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e,t,r){var n=void 0===r;switch(t.length){case 0:return n?e():e.call(r);case 1:return n?e(t[0]):e.call(r,t[0]);case 2:return n?e(t[0],t[1]):e.call(r,t[0],t[1]);case 3:return n?e(t[0],t[1],t[2]):e.call(r,t[0],t[1],t[2]);case 4:return n?e(t[0],t[1],t[2],t[3]):e.call(r,t[0],t[1],t[2],t[3])}return e.apply(r,t)}},function(e,t,r){var n=r(10),o=r(2)("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(n.Array===e||i[o]===e)}},function(e,t,r){var n=r(4);e.exports=function(e,t,r,o){try{return o?t(n(r)[0],r[1]):t(r)}catch(t){var i=e.return;throw void 0!==i&&n(i.call(e)),t}}},function(e,t,r){"use strict";var n=r(131),o=r(59),i=r(32),u={};r(7)(u,r(2)("iterator"),function(){return this}),e.exports=function(e,t,r){e.prototype=n(u,{next:o(1,r)}),i(e,t+" Iterator")}},function(e,t,r){var n=r(2)("iterator"),o=!1;try{var i=[7][n]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var r=!1;try{var i=[7],u=i[n]();u.next=function(){return{done:r=!0}},i[n]=function(){return u},e(i)}catch(e){}return r}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,r){var n=r(1),o=r(62).set,i=n.MutationObserver||n.WebKitMutationObserver,u=n.process,s=n.Promise,a="process"==r(15)(u);e.exports=function(){var e,t,r,c=function(){var n,o;for(a&&(n=u.domain)&&n.exit();e;){o=e.fn,e=e.next;try{o()}catch(n){throw e?r():t=void 0,n}}t=void 0,n&&n.enter()};if(a)r=function(){u.nextTick(c)};else if(!i||n.navigator&&n.navigator.standalone)if(s&&s.resolve){var f=s.resolve(void 0);r=function(){f.then(c)}}else r=function(){o.call(n,c)};else{var l=!0,p=document.createTextNode("");new i(c).observe(p,{characterData:!0}),r=function(){p.data=l=!l}}return function(n){var o={fn:n,next:void 0};t&&(t.next=o),e||(e=o,r()),t=o}}},function(e,t,r){"use strict";var n=r(56),o=r(133),i=r(136),u=r(64),s=r(54),a=Object.assign;e.exports=!a||r(29)(function(){var e={},t={},r=Symbol(),n="abcdefghijklmnopqrst";return e[r]=7,n.split("").forEach(function(e){t[e]=e}),7!=a({},e)[r]||Object.keys(a({},t)).join("")!=n})?function(e,t){for(var r=u(e),a=arguments.length,c=1,f=o.f,l=i.f;a>c;)for(var p,h=s(arguments[c++]),d=f?n(h).concat(f(h)):n(h),v=d.length,y=0;v>y;)l.call(h,p=d[y++])&&(r[p]=h[p]);return r}:a},function(e,t,r){var n=r(4),o=r(132),i=r(52),u=r(33)("IE_PROTO"),s=function(){},a=function(){var e,t=r(28)("iframe"),n=i.length;for(t.style.display="none",r(53).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" 46 | 47 | -------------------------------------------------------------------------------- /resources/js/components/FormField.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 114 | 115 | -------------------------------------------------------------------------------- /resources/js/components/IndexField.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /resources/js/tool.js: -------------------------------------------------------------------------------- 1 | Nova.booting((Vue, router) => { 2 | Vue.component('index-FieldCheckboxes', require('./components/IndexField')); 3 | Vue.component('detail-FieldCheckboxes', require('./components/DetailField')); 4 | Vue.component('form-FieldCheckboxes', require('./components/FormField')); 5 | 6 | }) 7 | -------------------------------------------------------------------------------- /resources/sass/tool.scss: -------------------------------------------------------------------------------- 1 | // Nova Tool CSS 2 | -------------------------------------------------------------------------------- /resources/views/navigation.blade.php: -------------------------------------------------------------------------------- 1 |

2 | 3 | 5 | 6 | 7 | {{ __('Roles & Permissions') }} 8 | 9 |

10 | 11 |
    12 | 13 |
  • 14 | 20 | {{ __('Roles') }} 21 | 22 |
  • 23 | 24 |
  • 25 | 31 | {{ __('Permissions') }} 32 | 33 |
  • 34 | 35 |
36 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | withMeta(['options' => $options]); 25 | } 26 | 27 | /** 28 | * Disable type casting of array keys to numeric values to return the unmodified keys. 29 | */ 30 | public function withGroups() 31 | { 32 | return $this->withMeta(['withGroups' => true]); 33 | } 34 | 35 | /** 36 | * Hydrate the given attribute on the model based on the incoming request. 37 | * 38 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 39 | * @param string $requestAttribute 40 | * @param object $model 41 | * @param string $attribute 42 | * @return void 43 | */ 44 | protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute) 45 | { 46 | if ($request->exists($requestAttribute)) { 47 | /** 48 | * When editing entries, they are returned as comma seperated string (unsure why). 49 | * As a result we need to include this check and explode the values if required. 50 | */ 51 | if (!is_array($choices = $request[$requestAttribute])) { 52 | $permissions = collect(explode(',', $choices))->reject(function ($name) { 53 | return empty($name); 54 | })->all(); 55 | } 56 | 57 | $model->syncPermissions($permissions); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authorize.php: -------------------------------------------------------------------------------- 1 | authorize($request) ? $next($request) : abort(403); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Nova/Permission.php: -------------------------------------------------------------------------------- 1 | mapWithKeys(function ($value, $key) { 78 | return [$key => $key]; 79 | }); 80 | 81 | $userResource = Nova::resourceForModel(getModelForGuard($this->guard_name)); 82 | 83 | return [ 84 | ID::make('Id', 'id') 85 | ->rules('required') 86 | ->hideFromIndex() 87 | , 88 | Text::make(__('Name'), 'name') 89 | ->rules(['required', 'string', 'max:255']) 90 | ->creationRules('unique:' . config('permission.table_names.permissions')) 91 | ->updateRules('unique:' . config('permission.table_names.permissions') . ',name,{{resourceId}}'), 92 | 93 | Text::make(__('Group'), 'group'), 94 | 95 | Select::make(__('Guard Name'), 'guard_name') 96 | ->options($guardOptions->toArray()) 97 | ->rules(['required', Rule::in($guardOptions)]), 98 | 99 | // DateTime::make(__('nova-permission-tool::permissions.created_at'), 'created_at')->exceptOnForms(), 100 | // DateTime::make(__('nova-permission-tool::permissions.updated_at'), 'updated_at')->exceptOnForms(), 101 | 102 | BelongsToMany::make(__('Roles'), 'roles', Role::class), 103 | MorphToMany::make($userResource::label(), 'users', $userResource)->searchable(), 104 | ]; 105 | } 106 | 107 | /** 108 | * Get the filters available for the resource. 109 | * 110 | * @param \Illuminate\Http\Request $request 111 | * @return array 112 | */ 113 | public function filters(Request $request) 114 | { 115 | return []; 116 | } 117 | 118 | public static function getModel() 119 | { 120 | //return app(PermissionRegistrar::class)->getPermissionClass(); 121 | } 122 | 123 | public static function label() 124 | { 125 | return __('Permissions'); 126 | } 127 | 128 | /** 129 | * Get the lenses available for the resource. 130 | * 131 | * @param \Illuminate\Http\Request $request 132 | * @return array 133 | */ 134 | public function lenses(Request $request) 135 | { 136 | return []; 137 | } 138 | 139 | public static function singularLabel() 140 | { 141 | return __('Permission'); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Nova/ResourceForUser.php: -------------------------------------------------------------------------------- 1 | user(); 19 | 20 | // Super Admin 21 | if ($user->isSuperAdmin()) { 22 | return $query; 23 | } 24 | 25 | // User Can View all Entries and is not restricted to its own 26 | if (!$user->hasPermissionTo('view own ' . parent::uriKey()) && $user->hasPermissionTo('view ' . parent::uriKey())) { 27 | return $query; 28 | } 29 | 30 | return parent::detailQuery($request, $query->where('user_id', $user->id)); 31 | } 32 | 33 | /** 34 | * Build an "index" query for the given resource. 35 | * 36 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 37 | * @param \Illuminate\Database\Eloquent\Builder $query 38 | * @return \Illuminate\Database\Eloquent\Builder 39 | */ 40 | public static function indexQuery(NovaRequest $request, $query) 41 | { 42 | $user = $request->user(); 43 | 44 | // Super Admin 45 | if ($user->isSuperAdmin()) { 46 | return $query; 47 | } 48 | 49 | // If the User has only Permission to view his own Entries, we scope the query. 50 | if ($user->hasPermissionTo('view own ' . parent::uriKey())) { 51 | return $query->where('user_id', $user->id); 52 | } 53 | 54 | return $query; 55 | } 56 | 57 | /** 58 | * Build a "relatable" query for the given resource. 59 | * 60 | * This query determines which instances of the model may be attached to other resources. 61 | * 62 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 63 | * @param \Illuminate\Database\Eloquent\Builder $query 64 | * @return \Illuminate\Database\Eloquent\Builder 65 | */ 66 | public static function relatableQuery(NovaRequest $request, $query) 67 | { 68 | $user = $request->user(); 69 | 70 | // Super Admin 71 | if ($user->isSuperAdmin()) { 72 | return parent::relatableQuery($request, $query); 73 | } 74 | 75 | // User Can View all Entries and is not restricted to its own 76 | if (!$user->hasPermissionTo('view own ' . parent::uriKey()) && $user->hasPermissionTo('view ' . parent::uriKey())) { 77 | return parent::relatableQuery($request, $query); 78 | } 79 | 80 | return parent::relatableQuery($request, $query->where('user_id', $user->id)); 81 | } 82 | 83 | /** 84 | * Build a Scout search query for the given resource. 85 | * 86 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 87 | * @param \Laravel\Scout\Builder $query 88 | * @return \Laravel\Scout\Builder 89 | */ 90 | public static function scoutQuery(NovaRequest $request, $query) 91 | { 92 | $user = $request->user(); 93 | 94 | // Super Admin 95 | if ($user->isSuperAdmin()) { 96 | return $query; 97 | } 98 | 99 | // User Can View all Entries and is not restricted to its own 100 | if (!$user->hasPermissionTo('view own ' . parent::uriKey()) && $user->hasPermissionTo('view ' . parent::uriKey())) { 101 | return $query; 102 | } 103 | 104 | return $query->where('user_id', $user->id); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Nova/Role.php: -------------------------------------------------------------------------------- 1 | mapWithKeys(function ($value, $key) { 77 | return [$key => $key]; 78 | }); 79 | 80 | $userResource = Nova::resourceForModel(getModelForGuard($this->guard_name)); 81 | 82 | return [ 83 | ID::make('Id', 'id') 84 | ->rules('required') 85 | ->hideFromIndex() 86 | , 87 | Text::make(__('Name'), 'name') 88 | ->rules(['required', 'string', 'max:255']) 89 | ->creationRules('unique:' . config('permission.table_names.roles')) 90 | ->updateRules('unique:' . config('permission.table_names.roles') . ',name,{{resourceId}}') 91 | 92 | , 93 | Select::make(__('Guard Name'), 'guard_name') 94 | ->options($guardOptions->toArray()) 95 | ->rules(['required', Rule::in($guardOptions)]) 96 | ->canSee(function ($request) { 97 | return $request->user()->isSuperAdmin(); 98 | }) 99 | , 100 | Checkboxes::make(__('Permissions'), 'prepared_permissions')->withGroups()->options(SpatiePermission::all()->map(function ($permission, $key) { 101 | return [ 102 | 'group' => __(ucfirst($permission->group)), 103 | 'option' => $permission->name, 104 | 'label' => __($permission->name), 105 | ]; 106 | })->groupBy('group')->toArray()) 107 | , 108 | Text::make(__('Users'), function () { 109 | return count($this->users); 110 | })->exceptOnForms(), 111 | MorphToMany::make($userResource::label(), 'users', $userResource)->searchable(), 112 | ]; 113 | } 114 | 115 | /** 116 | * Get the filters available for the resource. 117 | * 118 | * @param \Illuminate\Http\Request $request 119 | * @return array 120 | */ 121 | public function filters(Request $request) 122 | { 123 | return []; 124 | } 125 | 126 | public static function label() 127 | { 128 | return __('Roles'); 129 | } 130 | 131 | /** 132 | * Get the lenses available for the resource. 133 | * 134 | * @param \Illuminate\Http\Request $request 135 | * @return array 136 | */ 137 | public function lenses(Request $request) 138 | { 139 | return []; 140 | } 141 | 142 | public static function singularLabel() 143 | { 144 | return __('Role'); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/NovaPermissions.php: -------------------------------------------------------------------------------- 1 | roleResource, 33 | $this->permissionResource, 34 | ]); 35 | } 36 | 37 | /** 38 | * Get the displayable name of the resource tool. 39 | * 40 | * @return string 41 | */ 42 | public function name() 43 | { 44 | return 'Roles & Permissions'; 45 | } 46 | 47 | /** 48 | * @param string $permissionResource 49 | * @return mixed 50 | */ 51 | public function permissionResource(string $permissionResource) 52 | { 53 | $this->permissionResource = $permissionResource; 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * Build the view that renders the navigation links for the tool. 60 | * 61 | * @return \Illuminate\View\View 62 | */ 63 | public function renderNavigation() 64 | { 65 | return view('nova-permissions::navigation'); 66 | } 67 | 68 | /** 69 | * @param string $roleResource 70 | * @return mixed 71 | */ 72 | public function roleResource(string $roleResource) 73 | { 74 | $this->roleResource = $roleResource; 75 | 76 | return $this; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Policies/Policy.php: -------------------------------------------------------------------------------- 1 | isSuperAdmin()) { 14 | return true; 15 | } 16 | } 17 | 18 | /** 19 | * Determine whether the user can create models. 20 | * 21 | * @param \App\User $user 22 | * @return mixed 23 | */ 24 | public function create(User $user) 25 | { 26 | return $user->hasAnyPermission(['manage ' . static::$key, 'manage own ' . static::$key]); 27 | } 28 | 29 | /** 30 | * Determine whether the user can delete the model. 31 | * 32 | * @param \App\User $user 33 | * @return mixed 34 | */ 35 | public function delete(User $user, $model) 36 | { 37 | if ($user->hasPermissionTo('manage ' . static::$key) ) { 38 | return true; 39 | } 40 | 41 | if ($user->hasPermissionTo('manage own ' . static::$key)) { 42 | return $user->id == $model->user_id; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | /** 49 | * Determine whether the user can permanently delete the model. 50 | * 51 | * @param \App\User $user 52 | * @return mixed 53 | */ 54 | public function forceDelete(User $user, $model) 55 | { 56 | if ($user->hasPermissionTo('forceDelete ' . static::$key)) { 57 | return true; 58 | } 59 | 60 | if ($user->hasPermissionTo('manage own ' . static::$key) && $user->hasPermissionTo('forceDelete ' . static::$key)) { 61 | return $user->id == $model->user_id; 62 | } 63 | 64 | return false; 65 | } 66 | 67 | /** 68 | * Determine whether the user can restore the model. 69 | * 70 | * @param \App\User $user 71 | * @return mixed 72 | */ 73 | public function restore(User $user, $model) 74 | { 75 | if ($user->hasPermissionTo('restore ' . static::$key)) { 76 | return true; 77 | } 78 | 79 | if ($user->hasPermissionTo('manage own ' . static::$key) && $user->hasPermissionTo('restore ' . static::$key)) { 80 | return $user->id == $model->user_id; 81 | } 82 | 83 | return false; 84 | } 85 | 86 | /** 87 | * Determine whether the user can update the model. 88 | * 89 | * @param \App\User $user 90 | * @return mixed 91 | */ 92 | public function update(User $user, $model) 93 | { 94 | if ($user->hasPermissionTo('manage ' . static::$key)) { 95 | return true; 96 | } 97 | 98 | if ($user->hasPermissionTo('manage own ' . static::$key)) { 99 | return $user->id == $model->user_id; 100 | } 101 | 102 | return false; 103 | } 104 | 105 | /** 106 | * Determine whether the user can view the model. 107 | * 108 | * @param \App\User $user 109 | * @return mixed 110 | */ 111 | public function view(User $user, $model) 112 | { 113 | if ($user->hasPermissionTo('view ' . static::$key)) { 114 | return true; 115 | } 116 | 117 | if ($user->hasPermissionTo('view own ' . static::$key)) { 118 | return $user->id == $model->user_id; 119 | } 120 | 121 | return false; 122 | } 123 | 124 | /** 125 | * @param User $user 126 | */ 127 | public function viewAny(User $user) 128 | { 129 | return $user->hasPermissionTo('view ' . static::$key); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Role.php: -------------------------------------------------------------------------------- 1 | permissions->pluck('name')->toArray(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ToolServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__ . '/../resources/views', 'nova-permissions'); 22 | 23 | $this->publishes([ 24 | __DIR__ . '/../database/migrations/create_permission_tables.php.stub' => $this->getMigrationFileName($filesystem), 25 | ], 'migrations'); 26 | 27 | $this->publishes([ 28 | __DIR__ . '/../config/permission.php' => config_path('permission.php'), 29 | ], 'config'); 30 | 31 | $this->publishes([ 32 | __DIR__ . '/../database/seeds/RolesAndPermissionsSeeder.php.stub' => $this->app->databasePath() . "/seeds/RolesAndPermissionsSeeder.php", 33 | ], 'seeds'); 34 | 35 | $this->app->booted(function () { 36 | $this->routes(); 37 | }); 38 | 39 | Nova::serving(function (ServingNova $event) {}); 40 | } 41 | 42 | /** 43 | * Register any application services. 44 | * 45 | * @return void 46 | */ 47 | public function register() 48 | { 49 | $this->mergeConfigFrom( 50 | __DIR__ . '/../config/permission.php', 51 | 'permission' 52 | ); 53 | } 54 | 55 | /** 56 | * Returns existing migration file if found, else uses the current timestamp. 57 | * 58 | * @param Filesystem $filesystem 59 | * @return string 60 | */ 61 | protected function getMigrationFileName(Filesystem $filesystem): string 62 | { 63 | $timestamp = date('Y_m_d_His'); 64 | 65 | return Collection::make($this->app->databasePath() . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR) 66 | ->flatMap(function ($path) use ($filesystem) { 67 | return $filesystem->glob($path . '*_create_permission_tables.php'); 68 | })->push($this->app->databasePath() . "/migrations/{$timestamp}_create_permission_tables.php") 69 | ->first(); 70 | } 71 | 72 | /** 73 | * Register the tool's routes. 74 | * 75 | * @return void 76 | */ 77 | protected function routes() 78 | { 79 | if ($this->app->routesAreCached()) { 80 | return; 81 | } 82 | 83 | Route::middleware(['nova', Authorize::class]) 84 | ->prefix('nova-vendor/nova-permissions') 85 | ->group(__DIR__ . '/../routes/api.php'); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix.setPublicPath('dist') 4 | .js('resources/js/tool.js', 'js') 5 | .sass('resources/sass/tool.scss', 'css') 6 | --------------------------------------------------------------------------------