I'm having some troubles, passing roles and permission fo authenticated users to vue components.
My goal is restrict some elements, create, edit, delete buttons eg.
I've followed this tutorial: https://mmccaff.github.io/2018/11/03/laravel-permissions-in-vue-components/?fbclid=IwAR1Oja6yESRJS65huRXPa9SwO2oQdP3W8TwhE_pReUtaK_8rnVAOzixzwXM
But I have not success.
Here is part of my code:
RolesAndPermissionsSeeder
// create permissions
//Categorias
Permission::create(['name' => 'create category']);
Permission::create(['name' => 'update category']);
Permission::create(['name' => 'list categories']);
Permission::create(['name' => 'desactivate category']);
Permission::create(['name' => 'activate category']);
Permission::create(['name' => 'delete category']);
//Tareas
Permission::create(['name' => 'create task']);
Permission::create(['name' => 'update task']);
Permission::create(['name' => 'list tasks']);
Permission::create(['name' => 'desactivate task']);
Permission::create(['name' => 'activate task']);
Permission::create(['name' => 'delete task']);
//Tareas de planificador
$role = Role::create(['name' => 'planificador']);
$role->givePermissionTo(['list tasks']);
$role->givePermissionTo(['list categories']);
// or may be done by chaining
//TAreas de Aministrador
$role = Role::create(['name' => 'admin']);
//Categorias
$role->givePermissionTo(['create category']);
$role->givePermissionTo(['list categories']);
$role->givePermissionTo(['update category']);
$role->givePermissionTo(['desactivate category']);
$role->givePermissionTo(['activate category']);
I can limit to users, only menus, but when they access to list e.g. categories list, inside of they have the option of creating and the can even if their role doesn't have this option.
this is my admin panel.
In my user model, I have the function to get permissions
app/User.php:
public function getAllPermissionsAttribute() {
$permissions = [];
foreach (Permission::all() as $permission) {
if (Auth::user()->can($permission->name)) {
$permissions[] = $permission->name;
}
}
return $permissions;
}
In my principal.blade.php, I've put javascript array
......
</footer>
<!-- Bootstrap and necessary plugins -->
<script src="js/app.js"></script>
<script src="js/plantilla.js"></script>
<script>
window.Laravel = {!! json_encode([
'csrfToken' => csrf_token(),
'user' => Auth::user()
]) !!};
</script>
</body>
</html>
My mixin Permissions.vue
<script>
export default {
methods: {
$can(permissionName) {
return Permissions.indexOf(permissionName) !== -1;
},
},
};
</script>
Imported in app.js (then compiled)
import Permissions from './mixins/Permissions';
Vue.mixin(Permissions);
Finally, the can restriction in my component Categoria.vue
<div class="card">
<div class="card-header">
<i class="fa fa-align-justify"></i> CategorÃas
<div v-if="$can('create category')">
<button type="button" #click="abrirModal('categoria', 'registrar')" class="btn btn-secondary">
<i class="icon-plus"></i> Nuevo
</button>
</div>
</div>
This is the error
Sorry for extending the question, but I am trying to be more detailed.
When adding the scripts in the common file such as app.blade.php, make sure you are adding the following script:
<script>
#auth
window.Permissions = {!! json_encode(Auth::user()->allPermissions, true) !!};
#else
window.Permissions = [];
#endauth
</script>
<script src="{{ asset('js/app.js') }}"></script>
IMPORTANT: Make sure you are adding it before your main js file e.g (public/js/app.js).
Related
LONG POST WARNING
why isn't my form to create a new user not working? im using laravel 9 and livewire. This is my code:
this is the button from where i show the model to create a form:
<div class="py-4 space-y-4">
<div class="flex justify-between px-2">
<div class="w-1/4">
<x-jet-input placeholder="search will go here"/>
</div>
<div>
<x-jet-button wire:click="create">New Skill</x-jet-button>
</div>
</div>
</div>
This is the model that shows the form. this model is also used to edit a skill as per Caleb the livewire creator:
<form wire:submit.prevent="save">
<x-jet-dialog-modal wire:model.defer="showEditModal">
<x-slot name="title">Edit Skill</x-slot>
<x-slot name="content">
<div class="col-span-6 sm:col-span-4">
<x-jet-label for="name" value="{{ __('Skill name') }}" />
<select wire:model="editing.name"
id="name"
type="text"
class="mt-1 block w-full border-gray-300
focus:border-indigo-300 focus:ring
focus:ring-indigo-200 focus:ring-opacity-50
rounded-md shadow-sm">
#foreach(\App\Models\Skill::LANGUAGES as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
#endforeach
</select>
<x-jet-input-error for="editing.name" class="mt-2" />
<x-jet-label for="years" value="{{ __('Years of experience') }}" class="mt-4"/>
<x-jet-input wire:model="editing.years" id="years" type="number"
min="{{\App\Models\Skill::MIN_YEARS_OF_EXPERIENCE}}"
max="{{\App\Models\Skill::MAX_YEARS_OF_EXPERIENCE}}"
class="mt-1 block w-full"
placeholder="Years of experience"/>
<x-jet-input-error for="editing.years" class="mt-2" />
</div>
</x-slot>
<x-slot name="footer">
<x-jet-secondary-button wire:click="$set('showEditModal', false)" class="mr-2">Cancel</x-jet-secondary-button>
<x-jet-button type="submit">Save</x-jet-button>
</x-slot>
</x-jet-dialog-modal>
</form>
And this is my livewire component:
<?php
namespace App\Http\Livewire;
use App\Models\Skill;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class Skills extends Component
{
public $name ='';
public $showEditModal = false;
public Skill $editing;
public function rules()
{
return [
'editing.name' => 'required|in:'.collect(Skill::LANGUAGES)->keys()->implode(','),
'editing.years' => 'required|numeric|between:' . Skill::MIN_YEARS_OF_EXPERIENCE . ',' . Skill::MAX_YEARS_OF_EXPERIENCE,
];
}
public function render()
{
return view('livewire.skills', [
'skills' => Skill::where('user_id', auth()->id())->get(),
]);
}
public function mount(){
$this->editing = $this->makeBlankSkill();
}
public function makeBlankSkill(){
return Skill::make([
'name' => 'javascript',
'user_id' => auth()->user()->id,
]);
}
public function create(){
if ($this->editing->getKey()) $this->editing = $this->makeBlankSkill();
$this->showEditModal = true;
}
public function edit(Skill $skill) {
if ($this->editing->isNot($skill)) $this->editing = $skill;
$this->showEditModal = true;
}
public function save()
{
$this->validate();
$this->editing->save();
$this->showEditModal = false;
}
}
I keep getting SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value and i dont know why.
This is my modal:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Skill extends Model
{
use HasFactory;
const DEFAULT_OPTION = 'Please select a skill';
const LANGUAGES = [
'javascript' => 'JavaScript',
'php' => 'PHP',
'python' => 'Python',
'java' => 'Java',
'c#' => 'C#',
'c++' => 'C++',
'ruby' => 'Ruby',
'swift' => 'Swift',
'typescript' => 'TypeScript',
'rust' => 'Rust',
'go' => 'Go',
'kotlin' => 'Kotlin',
'scala' => 'Scala',
'dart' => 'Dart',
'r' => 'R',
'perl' => 'Perl',
'elixir' => 'Elixir',
'clojure' => 'Clojure',
'haskell' => 'Haskell',
'erlang' => 'Erlang',
'lisp' => 'Lisp',
'sql' => 'SQL',
'bash' => 'Bash',
'laravel' => 'Laravel',
'symfony' => 'Symfony',
'codeigniter' => 'CodeIgniter',
'yii' => 'Yii',
'zend' => 'Zend',
'cakephp' => 'CakePHP',
'fuelphp' => 'FuelPHP',
'slim' => 'Slim',
'lumen' => 'Lumen',
'phalcon' => 'Phalcon',
'silex' => 'Silex',
'express' => 'Express',
'koa' => 'Koa',
'hapi' => 'Hapi',
'meteor' => 'Meteor',
'angular' => 'Angular',
'ember' => 'Ember',
'react' => 'React',
'vue' => 'Vue',
'backbone' => 'Backbone',
'd3' => 'D3',
'threejs' => 'Three.js',
];
const MIN_YEARS_OF_EXPERIENCE = 1;
const MAX_YEARS_OF_EXPERIENCE = 50;
protected $fillable = [
'name', 'user_id', 'years'
];
public function user()
{
return $this->belongsTo(User::class);
}
}
Any help is greatly appriceated
I've done all there is to do.At least i hope. I've added the
$illable
array ive set the
'user_id' => auth()->user()->id,
Not sure what else im missing
public function save()
{
$this->validate();
$user = auth()->user();
$this->editing->user_id = $user->id;
$this->editing->save();
$this->showEditModal = false;
}
This was the answer for me
If user_id is null when creating a new Skill, this means there is no authenticated user. You can simply check by doing dd(auth()->id()). If you're logged in, this will return the primary key for your authentication model. If this is empty, you're simply not authenticated, and so you must first log in.
In the case your user_id is actually set, but it isn't arriving in your database upon saving, you'll have to check if the property user_id is correctly set on the Skill model's protected $fillable property.
If you dd($this->editing) right after mount, you can check the attributes of the model, and if the user_id is set, you know the error happens when saving to the database.
As it turns out, Livewire won't hydrate newly set properties on models. This is because Livewire "rehydrates" the models by simply re-fetching them from the database. This can be solved defining a rules property as shown here, directly relating to the model properties. This would ensure Livewire keeps the state of the updated properties.
I tried some solutions but I couldn't create a correct form to update the credit card.
I have created a special page to manage the credit card update.
Also, I would like to understand when for an automatic subscription renewal you have a credit card that has expired or otherwise "fails", what happens? Does the subscription continue making X attempts?
Right now, my code is as follows:
Controller
public function setupPayment(){
return view('update-card', [
'intent' => Auth::user()->createSetupIntent()
]);
}
public function updateExistingCreditCard(Request $request){
$key = \config('services.stripe.secret');
$stripe = new \Stripe\StripeClient($key);
$this->validate($request, [
'address' => 'nullable',
'name' => 'nullable'
]);
$user = Cashier::findBillable(Auth::user()->stripe_id);
//$user = Auth::user()->asStripeCustomer();
if($user->hasDefaultPaymentMethod()){
$stripe->customers->updateSource(
$user->stripe_id,
$user->id,
['name' => 'Jenny Rosen']
);
return redirect()->back()->with('success','Update.');
}
return redirect()->back()->with('danger','Error.');
}
HTML
<form id="payment-form" action="{{ route('update.new.credit.card') }}" method="POST">
#csrf
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
{{ __('Update Card') }}
</div>
<div class="card-body">
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Update Payment Method
</button>
</div>
</div>
</div>
</div>
</form>
const stripe = Stripe('{{ config('cashier.key') }}');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: cardHolderName.value
}
}
}
);
if (error) {
document.getElementById('card-button').disabled = false
} else {
let token = document.createElement('input')
token.setAttribute('type', 'hidden')
token.setAttribute('name', 'token')
token.setAttribute('value', setupIntent.payment_method)
form.appendChild(token)
form.submit();
}
});
UPDATE
In this new code, by inserting the metadata fields, they are correctly updated, so I guess the code is right. I am probably wrong with the parameters passed to update the card and make it default to the customer.
$stripeCustomer = Auth::user()->asStripeCustomer();
$paymentMethod = $stripe->customers->retrieve($stripeCustomer->id)->invoice_settings->default_payment_method;
if(Auth::user()->hasDefaultPaymentMethod()){
$stripe->customers->update($stripeCustomer->id, [
['invoice_settings' => ['default_payment_method' => $paymentMethod]]
]);
return redirect()->back()->with('success','Update.');
}
I am trying to use the snappy library with wkhtmltopdf to render a chart (LavaChart) on a generated PDF but I have not been able. The PDF generates fine but the chart does not show. If the view is not converted to PDF, the Chart is rendered as expected.
Below is my code for the LavaChart and Snappy.
The Chart Part
$chart = Lava::ColumnChart('Performance', $table, [
'title' => 'Performance Chart',
'png' => true,
'animation' => [
'startup' => true,
'easing' => 'inAndOut'
],
'titleTextStyle' => [
'fontName' => 'Arial',
'fontColor' => 'blue'
],
'legend' => [
'position' => 'top'
],
'vAxis' => [
'title' => 'Total Score'
],
'hAxis' => [
'title' => 'Class'
],
'events' => [
'ready' => 'getImageCallback'
],
'colors' => ['#3366CC','#DC2912', '#FF9900']
]);
The Snappy Part
$pdf = PDF::loadView('print.charts')->setPaper('portrait');
$pdf->setOption('enable-javascript', true);
$pdf->setOption('javascript-delay', 10000);
$pdf->setOption('no-stop-slow-scripts', true);
$pdf->setOption('page-size', 'A4');
$pdf->setOption('margin-left', 0);
$pdf->setOption('margin-right', 0);
$pdf->setOption('margin-top', 0);
$pdf->setOption('margin-bottom', 0);
$pdf->setOption('lowquality', false);
$pdf->setTimeout(1500);
$pdf->setOption('disable-smart-shrinking', true);
The View Part
<script type="text/javascript">
function getImageCallback (event, chart) {
console.log(chart.getImageURI());
}
</script>
<div id="chart" style="margin: 10px; height: 200px; width: 50%;"></div>
{!! Lava::render('ColumnChart', 'Performance', 'chart') !!}
Since the chart renders as expected when the view is not converted to pdf, I have reasons to believe the wkhtmltopdf does not execute the javascript has expected in the pdf version. I have the latest wkhtmltopdfinstalled but still no luck.
Library Version:
barryvdh/laravel-snappy: ^0.4.3
khill/lavacharts: 3.0.*
Any help will be appreciated, thanks.
I can show with a simple example, At first I have shown the chart on browser, The chart example is taken from Lavacharts docs(you can use yours). Keep a Note on
events with callback getImageCallback.
public function index(){
$lava = new Lavacharts;
$data = $lava->DataTable();
$data->addDateColumn('Day of Month')
->addNumberColumn('Projected')
->addNumberColumn('Official');
// Random Data For Example
for ($a = 1; $a < 20; $a++) {
$rowData = [
"2020-10-$a", rand(800,1000), rand(800,1000)
];
$data->addRow($rowData);
}
$lava->LineChart('Stocks', $data, [
'elementId' => 'stocks-div',
'title' => 'Stock Market Trends',
'animation' => [
'startup' => true,
'easing' => 'inAndOut'
],
'colors' => ['blue', '#F4C1D8'],
'events' => [
'ready' => 'getImageCallback'
]
]);
return view('charts-view', ['lava' => $lava]);
}
In view charts-view,
<div id="stocks-div">
<?= $lava->render('LineChart', 'Stocks', 'stocks-div'); ?>
</div>
<form action="{{ url('export-pdf') }}" method="post">
#csrf
<div class="form-group">
<input type="hidden" name="exportpdf" id="exportPDF">
<button class="btn btn-info" type="submit">Export as PDF</button>
</div>
</form>
<script type="text/javascript">
function getImageCallback (event, chart) {
console.log(chart.getImageURI());
document.getElementById("exportPDF").value = chart.getImageURI();
}
</script>
note the function name in script must be same as the value set for ready key in events in the controller. Upto this step you have done as well. I have passed the result obtained by as a hidden input field and posted the form to the controller.You can see in the diagram button export as PDF.
The url export-pdf calls the controller function exportPdf which willfinally generate the PDF. You need to pass the image (obtained as data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB .....) to the controller to pass it to the view as image.
In exportPdf,
public function exportPdf(Request $request){
$imageData = $request->get('exportpdf');
$pdf = SnappyPDF::loadView('export-pdf', ['imageData' => $imageData])->setPaper('a4')->setOrientation('portrait');
$pdf->setOption('lowquality', false);
$pdf->setTimeout(1500);
$pdf->setOption('disable-smart-shrinking', true);
return $pdf->download('stock-market.pdf');
}
The export-pdf blade view
<!DOCTYPE html>
<html lang="en">
<head>
<title>Stock Market</title>
</head>
<body>
<div class="container">
<div class="col">
<h2>Stock Market Detail</h2>
</div>
<div class="col">
<h4>Oct 2020</h4>
</div>
</div>
<img src="{{ $imageData }}" alt="image" width="720" height="230">
</body>
</html>
The final PDF obtained looks like,
PaymentController:
class PaymentController extends Controller
{
public function paymentProcess()
{
\Stripe\Stripe::setApiKey("sk_test_1M123Dge214GicrsW30adwG12X1");
$token=$_POST['stripetoken'];
$charge=\Stripe\Charge::create([
'amount'=>1000,
'currency'=>'usd',
'description'=>'Example charge',
'source'=>$token,
]);
}
}
index.blade.php:
<div class="content">
<div class="title m-b-md">
Laravel + Stripe
</div>
<div class="links">
<form action="/api/payment" method="POST">
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key='pk_test_zWfsa5k3D21cq1hPA39FmIdMJfkG3Taf74LD'
data-amount="100000"
data-name="My Name"
data-description="Test"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto"
data-currency="USD">
</script>
</form>
</div>
</div>
api.php:
Route::post('/payment','PaymentController#paymentProcess');
it keeps giving me this error Undefined index: stripetoken and on my
stripe test dashboard its still 0, no money has been added. Any help
is highly appreciated, Thank you in advance
You can use the Laravel Request to get the POST data to your Controller:
use Illuminate\Http\Request;
class PaymentController extends Controller
{
public function paymentProcess(Request $request)
{
\Stripe\Stripe::setApiKey("sk_test_1M123Dge214GicrsW30adwG12X1");
$token = $request->get('stripetoken');
$charge = \Stripe\Charge::create([
'amount' => 1000,
'currency' => 'usd',
'description' => 'Example charge',
'source' => $token,
]);
NOTE
You should always validate your request to make sure the data is there.
i have this code in my master page. how this variable to be called in my vue template. i think the way i call the variable is wrong pls correct me
<script>
window.App = {!! json_encode([
'user' => Auth::user()->id,
]) !!};
</script>
and this is my vue template
<template lang="html">
<div class="chat-message" v-if="message.user_from == window.App.user">
<div class="chatright">
{{message.user_from}} {{message.msg}}
</div>
</div>
<div class="chatleft" v-else>
{{message.user_from}} {{message.msg}}
</div>
</template>
You will call it like so:
window.App.user.id
And by the way, in your master page you only need this:
<script>
window.App = {!! json_encode([
'user' => Auth::user(),
]) !!};
</script>