Merge branch 'master' into issue-4242

Merged With Master
This commit is contained in:
devansh bawari 2020-11-24 12:27:35 +05:30
commit c70a324f40
55 changed files with 1174 additions and 319 deletions

View File

@ -37,6 +37,7 @@
"laravel/tinker": "^2.0",
"laravel/ui": "^2.0",
"maatwebsite/excel": "3.1.19",
"paypal/paypal-checkout-sdk": "1.0.1",
"prettus/l5-repository": "^2.6",
"tymon/jwt-auth": "^1.0.0"
},

90
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d666b5a314de775c8d05fb2f94aa9e01",
"content-hash": "fc14e417d44f0efbc8d33e5ac654fba5",
"packages": [
{
"name": "algolia/algoliasearch-client-php",
@ -3311,6 +3311,94 @@
],
"time": "2020-10-11T21:42:15+00:00"
},
{
"name": "paypal/paypal-checkout-sdk",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/paypal/Checkout-PHP-SDK.git",
"reference": "ed6a55075448308b87a8b59dcb7fedf04a048cb1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paypal/Checkout-PHP-SDK/zipball/ed6a55075448308b87a8b59dcb7fedf04a048cb1",
"reference": "ed6a55075448308b87a8b59dcb7fedf04a048cb1",
"shasum": ""
},
"require": {
"paypal/paypalhttp": "1.0.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"PayPalCheckoutSdk\\": "lib/PayPalCheckoutSdk",
"Sample\\": "samples/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"https://github.com/paypal/Checkout-PHP-SDK/blob/master/LICENSE"
],
"authors": [
{
"name": "PayPal",
"homepage": "https://github.com/paypal/Checkout-PHP-SDK/contributors"
}
],
"description": "PayPal's PHP SDK for Checkout REST APIs",
"homepage": "http://github.com/paypal/Checkout-PHP-SDK/",
"keywords": [
"checkout",
"orders",
"payments",
"paypal",
"rest",
"sdk"
],
"time": "2019-11-07T23:16:44+00:00"
},
{
"name": "paypal/paypalhttp",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/paypal/paypalhttp_php.git",
"reference": "1ad9b846a046f09d6135cbf2cbaa7701bbc630a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paypal/paypalhttp_php/zipball/1ad9b846a046f09d6135cbf2cbaa7701bbc630a3",
"reference": "1ad9b846a046f09d6135cbf2cbaa7701bbc630a3",
"shasum": ""
},
"require": {
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"wiremock-php/wiremock-php": "1.43.2"
},
"type": "library",
"autoload": {
"psr-4": {
"PayPalHttp\\": "lib/PayPalHttp"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PayPal",
"homepage": "https://github.com/paypal/paypalhttp_php/contributors"
}
],
"time": "2019-11-06T21:27:12+00:00"
},
{
"name": "phenx/php-font-lib",
"version": "0.5.2",

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/admin.js": "/js/admin.js?id=c849d3c02afc5071f801",
"/js/admin.js": "/js/admin.js?id=e4c3d46b82083613b407",
"/css/admin.css": "/css/admin.css?id=f2b20e4283a639808ef6"
}

View File

@ -2,7 +2,7 @@
<div class="control-group" :class="[errors.has(name) ? 'has-error' : '']">
<label :for="name" :class="checkValidations">
{{ label }}
<span class="locale"> [{{ channel_locale }}] </span>
<span class="locale"> {{ channel_locale ? `[${channel_locale}]` : '' }} </span>
</label>
<select v-if="this.options.length" v-validate="checkValidations" class="control" :id = "name" :name = "name" v-model="savedValue"
@ -10,8 +10,15 @@
<option v-for='(option, index) in this.options' :value="option.value"> {{ option.title }} </option>
</select>
<input v-else type="text" class="control" v-validate="checkValidations" :id = "name" :name = "name" v-model="savedValue"
:data-vv-as="label">
<input v-else
type="text"
class="control"
:id="name"
:name="name"
:placeholder="info"
v-validate="checkValidations"
v-model="savedValue"
:data-vv-as="label">
<span class="control-error" v-if="errors.has(name)">
{{ errors.first(name) }}
@ -24,6 +31,7 @@ export default {
props: [
'name',
'label',
'info',
'options',
'result',
'validations',

View File

@ -529,7 +529,13 @@ return [
'checkbox' => 'خانة اختيار',
'multiselect' => 'تحديد متعدد',
'new-option' => 'خيار جديد',
'is-default' => 'افتراضي'
'is-default' => 'افتراضي',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1362,9 +1368,6 @@ return [
'custom-scripts' => 'Custom Scripts',
'custom-css' => 'Custom CSS',
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.',
'mailing-address' => 'أرسل الشيك إلى',
'instructions' => 'تعليمات',
'custom-scripts' => 'البرامج النصية المخصصة',
@ -1372,7 +1375,11 @@ return [
'custom-javascript' => 'جافا سكريبت مخصص',
'paypal-smart-button' => 'زر PayPal الذكي',
'client-id' => 'معرف العميل',
'client-id-info' => 'استخدم "sb" للاختبار.'
'client-id-info' => 'استخدم "sb" للاختبار.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -521,6 +521,12 @@ return [
'multiselect' => 'Multiselect',
'new-option' => 'Neue Option',
'is-default' => 'Ist Standard',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' =>
[
@ -1343,7 +1349,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
],
],
];

View File

@ -533,6 +533,12 @@ return [
'all-group' => 'All Groups',
'fixed' => 'Fixed',
'discount' => 'Discount',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1370,7 +1376,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -522,7 +522,13 @@ return [
'checkbox' => 'Opción múltiple',
'multiselect' => 'Lista de selección múltiple',
'new-option' => 'Nueva opción',
'is-default' => 'Es predeterminada'
'is-default' => 'Es predeterminada',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1325,7 +1331,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -527,7 +527,13 @@ return [
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option',
'is-default' => 'Is Default'
'is-default' => 'Is Default',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1361,7 +1367,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -530,7 +530,13 @@ return [
'multiselect' => 'Multiselect',
'new-option' => 'Nuova Opzione',
'is-default' => 'È Predefinita',
'actions' => 'Azioni'
'actions' => 'Azioni',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1366,7 +1372,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -529,6 +529,12 @@ return [
'multiselect' => 'Multiselect',
'new-option' => 'Nieuwe optie',
'is-default' => 'is standaard',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1361,7 +1367,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -528,7 +528,13 @@ return [
'checkbox' => 'Pole wyboru',
'multiselect' => 'Wielokrotny wybór',
'new-option' => 'Nowa opcja',
'is-default' => 'Jest domyślny'
'is-default' => 'Jest domyślny',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1350,7 +1356,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -527,7 +527,13 @@ return [
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option',
'is-default' => 'Is Default'
'is-default' => 'Is Default',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1364,7 +1370,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -523,7 +523,13 @@ return [
'checkbox' => 'Onay Kutusu',
'multiselect' => 'Çoklu Seçim',
'new-option' => 'Yeni Seçenek',
'is-default' => 'Varsayılan'
'is-default' => 'Varsayılan',
'remove-image-btn-title' => 'Remove Image',
'videos' => 'Videos',
'video' => 'Video',
'add-video-btn-title' => 'Add Video',
'remove-video-btn-title' => 'Remove Video',
'not-support-video' => 'Your browser does not support the video tag.',
],
'attributes' => [
@ -1347,7 +1353,11 @@ return [
'custom-javascript' => 'Custom Javascript',
'paypal-smart-button' => 'PayPal',
'client-id' => 'Client Id',
'client-id-info' => 'Use "sb" for testing.'
'client-id-info' => 'Use "sb" for testing.',
'client-secret' => 'Client Secret',
'client-secret-info' => 'Add your secret key here',
'accepted-currencies' => 'Accepted currencies',
'accepted-currencies-info' => 'Add currency code comma seperated e.g. USD,INR,...'
]
]
];

View File

@ -54,7 +54,9 @@
<img class="preview" :src="imageData" v-if="imageData.length > 0">
<label class="remove-image" @click="removeImage()">Remove Image</label>
<label class="remove-image" @click="removeImage()">
{{ __('admin::app.catalog.products.remove-image-btn-title') }}
</label>
</label>
</script>

View File

@ -0,0 +1,198 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.videos.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.videos') }}'" :active="false">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.videos.controls.before', ['product' => $product]) !!}
<div class="control-group {!! $errors->has('videos.*') ? 'has-error' : '' !!}">
<label>{{ __('admin::app.catalog.products.video') }}</label>
<product-video></product-video>
<span class="control-error" v-if="{!! $errors->has('videos.*') !!}">
@php $count=1 @endphp
@foreach ($errors->get('videos.*') as $key => $message)
@php echo str_replace($key, 'Video'.$count, $message[0]); $count++ @endphp
@endforeach
</span>
</div>
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.videos.controls.after', ['product' => $product]) !!}
</div>
</accordian>
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.videos.after', ['product' => $product]) !!}
@push('scripts')
@parent
<script type="text/x-template" id="product-video-template">
<div>
<div class="image-wrapper">
<product-video-item
v-for='(video, index) in items'
:key='video.id'
:video="video"
@onRemoveVideo="removeVideo($event)"
@onVideoSelected="videoSelected($event)"
></product-video-item>
</div>
<label class="btn btn-lg btn-primary" style="display: inline-block; width: auto" @click="createFileType">
{{ __('admin::app.catalog.products.add-video-btn-title') }}
</label>
</div>
</script>
<script type="text/x-template" id="product-video-item-template">
<label class="image-item" v-bind:class="{ 'has-image': videoData.length > 0 }">
<input type="hidden" :name="'videos[' + video.id + ']'" v-if="! new_video"/>
<input type="file" v-validate="'mimes:video/*'" accept="video/*" :name="'videos[]'" ref="videoInput" :id="_uid" @change="addVideoView($event)" multiple="multiple"/>
<video class="preview" v-if="videoData.length > 0" width="200" height="160" controls>
<source :src="videoData" type="video/mp4">
{{ __('admin::app.catalog.products.not-support-video') }}
</video>
<label class="remove-image" @click="removeVideo()">
{{ __('admin::app.catalog.products.remove-video-btn-title') }}
</label>
</label>
</script>
<script>
Vue.component('product-video', {
template: '#product-video-template',
data: function() {
return {
videos: @json($product->videos),
videoCount: 0,
items: []
}
},
computed: {
finalInputName: function() {
return 'videos[' + this.video.id + ']';
}
},
created: function() {
var this_this = this;
this.videos.forEach(function(video) {
this_this.items.push(video)
this_this.videoCount++;
});
},
methods: {
createFileType: function() {
var this_this = this;
this.videoCount++;
this.items.push({'id': 'video_' + this.videoCount});
},
removeVideo (video) {
let index = this.items.indexOf(video)
Vue.delete(this.items, index);
},
videoSelected: function(event) {
var this_this = this;
Array.from(event.files).forEach(function(video, index) {
if (index) {
this_this.videoCount++;
this_this.items.push({'id': 'video_' + this_this.videoCount, file: video});
}
});
}
}
});
Vue.component('product-video-item', {
template: '#product-video-item-template',
props: {
video: {
type: Object,
required: false,
default: null
},
},
data: function() {
return {
videoData: '',
new_video: 0
}
},
mounted () {
if (this.video.id && this.video.url) {
this.videoData = this.video.url;
} else if (this.video.id && this.video.file) {
this.readFile(this.video.file);
}
},
computed: {
finalInputName: function() {
return this.inputName + '[' + this.video.id + ']';
}
},
methods: {
addVideoView: function() {
var videoInput = this.$refs.videoInput;
if (videoInput.files && videoInput.files[0]) {
if (videoInput.files[0].type.includes('video/')) {
this.readFile(videoInput.files[0])
if (videoInput.files.length > 1) {
this.$emit('onVideoSelected', videoInput)
}
} else {
videoInput.value = "";
alert('Only videos (.mp4, .mov, .ogg ..) are allowed.');
}
}
},
readFile: function(video) {
var reader = new FileReader();
reader.onload = (e) => {
this.videoData = e.target.result;
}
reader.readAsDataURL(video);
this.new_video = 1;
},
removeVideo: function() {
this.$emit('onRemoveVideo', this.video)
}
}
});
</script>
@endpush

View File

@ -72,6 +72,7 @@
<required-if
:name = "'{{ $firstField }}[{{ $secondField }}][{{ $thirdField }}][{{ $field['name'] }}]'"
:label = "'{{ trans($field['title']) }}'"
:info = "'{{ trans(isset($field['info']) ? $field['info'] : '') }}'"
:options = '@json($field['options'])'
:result = "'{{ $selectedOption }}'"
:validations = "'{{ $validations }}'"

View File

@ -39,7 +39,7 @@
<div class="dropdown-list bottom-right">
<div class="control-group">
<select class="control" id="locale-switcher" onChange="window.location.href = this.value" style="margin-left: 30px;">
<select class="control" onChange="window.location.href = this.value" style="margin-left: 30px;">
@foreach (core()->getAllLocales() as $localeModel)
<option value="{{ '?admin_locale=' . $localeModel->code }}" {{ ($localeModel->code) == $locale ? 'selected' : '' }}>

View File

@ -12,6 +12,7 @@ use Webkul\Product\Datatypes\CartItemValidationResult;
use Webkul\Product\Helpers\ProductImage;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Type\Virtual;
@ -44,6 +45,7 @@ class Booking extends Virtual
'admin::catalog.products.accordians.channels',
'bookingproduct::admin.catalog.products.accordians.booking',
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**
@ -54,9 +56,10 @@ class Booking extends Virtual
* @param \Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository
* @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\BookingProduct\Repositories\BookingProductRepository $bookingProductRepository
* @param \Webkul\BookingProduct\Helpers\BookingHelper $bookingHelper
* @param \Webkul\BookingProduct\Helpers\BookingHelper $bookingHelper
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
* @return void
*/
public function __construct(
@ -67,7 +70,8 @@ class Booking extends Virtual
ProductImageRepository $productImageRepository,
ProductImage $productImageHelper,
BookingProductRepository $bookingProductRepository,
BookingHelper $bookingHelper
BookingHelper $bookingHelper,
ProductVideoRepository $productVideoRepository
)
{
parent::__construct(
@ -76,7 +80,8 @@ class Booking extends Virtual
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$productImageHelper
$productImageHelper,
$productVideoRepository
);
$this->bookingProductRepository = $bookingProductRepository;

View File

@ -6,6 +6,7 @@ use Exception;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Event;
use Illuminate\Validation\ValidationException;
use Webkul\CartRule\Repositories\CartRuleRepository;
use Webkul\CartRule\Repositories\CartRuleCouponRepository;
@ -113,30 +114,39 @@ class CartRuleController extends Controller
*/
public function store()
{
$this->validate(request(), [
'name' => 'required',
'channels' => 'required|array|min:1',
'customer_groups' => 'required|array|min:1',
'coupon_type' => 'required',
'use_auto_generation' => 'required_if:coupon_type,==,1',
'coupon_code' => 'required_if:use_auto_generation,==,0|unique:cart_rule_coupons,code',
'starts_from' => 'nullable|date',
'ends_till' => 'nullable|date|after_or_equal:starts_from',
'action_type' => 'required',
'discount_amount' => 'required|numeric',
]);
try {
$this->validate(request(), [
'name' => 'required',
'channels' => 'required|array|min:1',
'customer_groups' => 'required|array|min:1',
'coupon_type' => 'required',
'use_auto_generation' => 'required_if:coupon_type,==,1',
'coupon_code' => 'required_if:use_auto_generation,==,0|unique:cart_rule_coupons,code',
'starts_from' => 'nullable|date',
'ends_till' => 'nullable|date|after_or_equal:starts_from',
'action_type' => 'required',
'discount_amount' => 'required|numeric',
]);
$data = request()->all();
$data = request()->all();
Event::dispatch('promotions.cart_rule.create.before');
Event::dispatch('promotions.cart_rule.create.before');
$cartRule = $this->cartRuleRepository->create($data);
$cartRule = $this->cartRuleRepository->create($data);
Event::dispatch('promotions.cart_rule.create.after', $cartRule);
Event::dispatch('promotions.cart_rule.create.after', $cartRule);
session()->flash('success', trans('admin::app.response.create-success', ['name' => 'Cart Rule']));
session()->flash('success', trans('admin::app.response.create-success', ['name' => 'Cart Rule']));
return redirect()->route($this->_config['redirect']);
return redirect()->route($this->_config['redirect']);
} catch (ValidationException $e) {
if ($firstError = collect($e->errors())->first()) {
session()->flash('error', $firstError[0]);
}
}
return redirect()->back();
}
/**
@ -163,30 +173,39 @@ class CartRuleController extends Controller
*/
public function update(Request $request, $id)
{
$this->validate(request(), [
'name' => 'required',
'channels' => 'required|array|min:1',
'customer_groups' => 'required|array|min:1',
'coupon_type' => 'required',
'use_auto_generation' => 'required_if:coupon_type,==,1',
'coupon_code' => 'required_if:use_auto_generation,==,0|unique:cart_rule_coupons,code,' . $id,
'starts_from' => 'nullable|date',
'ends_till' => 'nullable|date|after_or_equal:starts_from',
'action_type' => 'required',
'discount_amount' => 'required|numeric',
]);
try {
$this->validate(request(), [
'name' => 'required',
'channels' => 'required|array|min:1',
'customer_groups' => 'required|array|min:1',
'coupon_type' => 'required',
'use_auto_generation' => 'required_if:coupon_type,==,1',
'coupon_code' => 'required_if:use_auto_generation,==,0|unique:cart_rule_coupons,code,' . $id,
'starts_from' => 'nullable|date',
'ends_till' => 'nullable|date|after_or_equal:starts_from',
'action_type' => 'required',
'discount_amount' => 'required|numeric',
]);
$cartRule = $this->cartRuleRepository->findOrFail($id);
$cartRule = $this->cartRuleRepository->findOrFail($id);
Event::dispatch('promotions.cart_rule.update.before', $cartRule);
Event::dispatch('promotions.cart_rule.update.before', $cartRule);
$cartRule = $this->cartRuleRepository->update(request()->all(), $id);
$cartRule = $this->cartRuleRepository->update(request()->all(), $id);
Event::dispatch('promotions.cart_rule.update.after', $cartRule);
Event::dispatch('promotions.cart_rule.update.after', $cartRule);
session()->flash('success', trans('admin::app.response.update-success', ['name' => 'Cart Rule']));
session()->flash('success', trans('admin::app.response.update-success', ['name' => 'Cart Rule']));
return redirect()->route($this->_config['redirect']);
return redirect()->route($this->_config['redirect']);
} catch (ValidationException $e) {
if ($firstError = collect($e->errors())->first()) {
session()->flash('error', $firstError[0]);
}
}
return redirect()->back();
}
/**

View File

@ -2,7 +2,7 @@
return [
'paypal_smart_button' => [
'code' => 'paypal_smart_button',
'title' => 'PayPal',
'title' => 'PayPal Smart Button',
'description' => 'PayPal',
'client_id' => 'sb',
'class' => 'Webkul\Paypal\Payment\SmartButton',

View File

@ -86,6 +86,20 @@ return [
'type' => 'depends',
'depend' => 'active:1',
'validation' => 'required_if:active,1',
], [
'name' => 'client_secret',
'title' => 'admin::app.admin.system.client-secret',
'info' => 'admin::app.admin.system.client-secret-info',
'type' => 'depends',
'depend' => 'active:1',
'validation' => 'required_if:active,1',
], [
'name' => 'accepted_currencies',
'title' => 'admin::app.admin.system.accepted-currencies',
'info' => 'admin::app.admin.system.accepted-currencies-info',
'type' => 'depends',
'depend' => 'active:1',
'validation' => 'required_if:active,1',
], [
'name' => 'active',
'title' => 'admin::app.admin.system.status',
@ -93,7 +107,13 @@ return [
'validation' => 'required',
'channel_based' => false,
'locale_based' => true
], [
], [
'name' => 'sandbox',
'title' => 'admin::app.admin.system.sandbox',
'type' => 'boolean',
'channel_based' => false,
'locale_based' => true,
], [
'name' => 'sort',
'title' => 'admin::app.admin.system.sort_order',
'type' => 'select',

View File

@ -3,11 +3,20 @@
namespace Webkul\Paypal\Http\Controllers;
use Webkul\Checkout\Facades\Cart;
use Webkul\Paypal\Payment\SmartButton;
use Webkul\Sales\Repositories\OrderRepository;
use Webkul\Sales\Repositories\InvoiceRepository;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
class SmartButtonController extends Controller
{
/**
* SmartButton object
*
* @var \Webkul\Paypal\Payment\SmartButton
*/
protected $smartButtonClient;
/**
* OrderRepository object
*
@ -30,6 +39,7 @@ class SmartButtonController extends Controller
* @return void
*/
public function __construct(
SmartButton $smartButtonClient,
OrderRepository $orderRepository,
InvoiceRepository $invoiceRepository
)
@ -37,14 +47,67 @@ class SmartButtonController extends Controller
$this->orderRepository = $orderRepository;
$this->invoiceRepository = $invoiceRepository;
$this->smartButtonClient = $smartButtonClient->client();
}
/**
* Success payment
* Success payment.
*
* @return \Illuminate\Http\JsonResponse
*/
public function details()
public function details(OrdersCreateRequest $request)
{
$request->prefer('return=representation');
$request->body = $this->buildRequestBody();
$response = $this->smartButtonClient->execute($request);
return response()->json($response);
}
/**
* Save order.
*
* @return \Illuminate\Http\Response
*/
public function saveOrder()
{
if (Cart::hasError()) {
return response()->json(['redirect_url' => route('shop.checkout.cart.index')], 403);
}
try {
Cart::collectTotals();
$this->validateOrder();
$order = $this->orderRepository->create(Cart::prepareDataForOrder());
$this->orderRepository->update(['status' => 'processing'], $order->id);
if ($order->canInvoice()) {
$this->invoiceRepository->create($this->prepareInvoiceData($order));
}
Cart::deActivateCart();
session()->flash('order', $order);
return response()->json([
'success' => true,
]);
} catch (\Exception $e) {
session()->flash('error', trans('shop::app.common.error'));
throw $e;
}
}
/**
* Build request body.
*
* @return array
*/
protected function buildRequestBody()
{
$cart = Cart::getCart();
@ -143,12 +206,12 @@ class SmartButtonController extends Controller
}
/**
* Return cart items
* Return cart items.
*
* @param string $cart
* @return array
*/
public function getLineItems($cart)
protected function getLineItems($cart)
{
$lineItems = [];
@ -169,12 +232,12 @@ class SmartButtonController extends Controller
}
/**
* Return convert multiple address lines into 2 address lines
* Return convert multiple address lines into 2 address lines.
*
* @param string $address
* @return array
*/
public function getAddressLines($address)
protected function getAddressLines($address)
{
$address = explode(PHP_EOL, $address, 2);
@ -190,47 +253,7 @@ class SmartButtonController extends Controller
}
/**
* Save order
*
* @return \Illuminate\Http\Response
*/
public function saveOrder()
{
if (Cart::hasError()) {
return response()->json(['redirect_url' => route('shop.checkout.cart.index')], 403);
}
try {
Cart::collectTotals();
$this->validateOrder();
$cart = Cart::getCart();
$order = $this->orderRepository->create(Cart::prepareDataForOrder());
$this->orderRepository->update(['status' => 'processing'], $order->id);
if ($order->canInvoice()) {
$invoice = $this->invoiceRepository->create($this->prepareInvoiceData($order));
}
Cart::deActivateCart();
session()->flash('order', $order);
return response()->json([
'success' => true,
]);
} catch (\Exception $e) {
session()->flash('error', trans('shop::app.common.error'));
throw $e;
}
}
/**
* Prepares order's invoice data for creation
* Prepares order's invoice data for creation.
*
* @param \Webkul\Sales\Models\Order $order
* @return array
@ -247,14 +270,20 @@ class SmartButtonController extends Controller
}
/**
* Validate order before creation
* Validate order before creation.
*
* @return void|\Exception
*/
public function validateOrder()
protected function validateOrder()
{
$cart = Cart::getCart();
$minimumOrderAmount = (int) core()->getConfigData('sales.orderSettings.minimum-order.minimum_order_amount') ?? 0;
if (! $cart->checkMinimumOrder()) {
throw new \Exception(trans('shop::app.checkout.cart.minimum-order-message', ['amount' => core()->currency($minimumOrderAmount)]));
}
if ($cart->haveStockableItems() && ! $cart->shipping_address) {
throw new \Exception(trans('Please check shipping address.'));
}

View File

@ -2,15 +2,41 @@
namespace Webkul\Paypal\Payment;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
class SmartButton extends Paypal
{
/**
* Payment method code
* Client ID.
*
* @var string
*/
protected $clientId;
/**
* Client secret.
*
* @var string
*/
protected $clientSecret;
/**
* Payment method code.
*
* @var string
*/
protected $code = 'paypal_smart_button';
/**
* Constructor.
*/
public function __construct()
{
$this->initialize();
}
/**
* Return paypal redirect url
*
@ -19,4 +45,39 @@ class SmartButton extends Paypal
public function getRedirectUrl()
{
}
/**
* Returns PayPal HTTP client instance with environment that has access
* credentials context. Use this instance to invoke PayPal APIs, provided the
* credentials have access.
*/
public function client()
{
return new PayPalHttpClient($this->environment());
}
/**
* Set up and return PayPal PHP SDK environment with PayPal access credentials.
* This sample uses SandboxEnvironment. In production, use LiveEnvironment.
*/
protected function environment()
{
$isSandbox = $this->getConfigData('sandbox') ?: false;
if ($isSandbox) {
return new SandboxEnvironment($this->clientId, $this->clientSecret);
}
return new ProductionEnvironment($this->clientId, $this->clientSecret);
}
/**
* Initialize properties.
*/
protected function initialize()
{
$this->clientId = $this->getConfigData('client_id') ?: '';
$this->clientSecret = $this->getConfigData('client_secret') ?: '';
}
}

View File

@ -1,96 +1,95 @@
@if (request()->route()->getName() == 'shop.checkout.onepage.index' && core()->getConfigData('sales.paymentmethods.paypal_smart_button.active'))
<script src="https://www.paypal.com/sdk/js?client-id={{core()->getConfigData('sales.paymentmethods.paypal_smart_button.client_id')}}" data-partner-attribution-id="Bagisto_Cart"></script>
@if (request()->route()->getName() == 'shop.checkout.onepage.index')
<style>
.component-frame.visible {
z-index: 1 !important;
}
</style>
@php
$clientId = core()->getConfigData('sales.paymentmethods.paypal_smart_button.client_id');
$acceptedCurrency = core()->getConfigData('sales.paymentmethods.paypal_smart_button.accepted_currencies');
@endphp
<script>
window.onload = (function() {
eventBus.$on('after-payment-method-selected', function(payment) {
if (payment.method != 'paypal_smart_button') {
$('.paypal-buttons').remove();
<script src="https://www.paypal.com/sdk/js?client-id={{ $clientId }}&currency={{ $acceptedCurrency }}" data-partner-attribution-id="Bagisto_Cart"></script>
return;
}
<style>
.component-frame.visible {
z-index: 1 !important;
}
</style>
if (typeof paypal == 'undefined') {
window.flashMessages = [{'type': 'alert-error', 'message': "SDK Validation error: 'client-id not recognized for either production or sandbox: {{core()->getConfigData('sales.paymentmethods.paypal_smart_button.client_id')}}'" }];
<script>
window.onload = (function() {
eventBus.$on('after-payment-method-selected', function(payment) {
if (payment.method != 'paypal_smart_button') {
$('.paypal-buttons').remove();
window.flashMessages.alertMessage = "SDK Validation error: 'client-id not recognized for either production or sandbox: {{core()->getConfigData('sales.paymentmethods.paypal_smart_button.client_id')}}'";
return;
}
app.addFlashMessages();
if (typeof paypal == 'undefined') {
window.flashMessages = [{'type': 'alert-error', 'message': "SDK Validation error: 'client-id not recognized for either production or sandbox: {{core()->getConfigData('sales.paymentmethods.paypal_smart_button.client_id')}}'" }];
return;
}
var options = {
style: {
layout: 'vertical',
shape: 'rect',
},
enableStandardCardFields: false,
createOrder: function(data, actions) {
return window.axios.get("{{ route('paypal.smart_button.details') }}")
.then(function(response) {
return actions.order.create(response.data);
})
.catch(function (error) {})
},
// Finalize the transaction
onApprove: function(data, actions) {
app.showLoader();
return actions.order.capture()
.then(function(response) {
if (response.error == 'INSTRUMENT_DECLINED') {
return actions.restart();
} else {
return response;
}
})
.then(function(details) {
return window.axios.post("{{ route('paypal.smart_button.save_order') }}", {
'_token': "{{ csrf_token() }}",
'data' : details
})
.then(function(response) {
if (response.data.success) {
if (response.data.redirect_url) {
window.location.href = response.data.redirect_url;
} else {
window.location.href = "{{ route('shop.checkout.success') }}";
}
}
app.hideLoader()
})
.catch(function (error) {
window.location.href = "{{ route('shop.checkout.cart.index') }}";
})
});
},
onCancel: function (data) {
console.log('Canceled payment')
},
onError: function (err) {
window.flashMessages = [{'type': 'alert-error', 'message': err }];
window.flashMessages.alertMessage = err;
window.flashMessages.alertMessage = "SDK Validation error: 'client-id not recognized for either production or sandbox: {{core()->getConfigData('sales.paymentmethods.paypal_smart_button.client_id')}}'";
app.addFlashMessages();
}
};
paypal.Buttons(options).render(".paypal-button-container");
return;
}
let options = {
style: {
layout: 'vertical',
shape: 'rect',
},
enableStandardCardFields: false,
createOrder: function(data, actions) {
return window.axios.get("{{ route('paypal.smart_button.details') }}")
.then(function(response) {
return response.data.result;
})
.then(function(orderData) {
return orderData.id;
})
.catch(function (error) {})
},
onApprove: function(data, actions) {
app.showLoader();
window.axios.post("{{ route('paypal.smart_button.save_order') }}", {
'_token': "{{ csrf_token() }}",
'data' : data
})
.then(function(response) {
if (response.data.success) {
if (response.data.redirect_url) {
window.location.href = response.data.redirect_url;
} else {
window.location.href = "{{ route('shop.checkout.success') }}";
}
}
app.hideLoader()
})
.catch(function (error) {
window.location.href = "{{ route('shop.checkout.cart.index') }}";
})
},
onCancel: function (data) {
console.log('Canceled payment...');
},
onError: function (err) {
window.flashMessages = [{'type': 'alert-error', 'message': err }];
window.flashMessages.alertMessage = err;
app.addFlashMessages();
}
};
paypal.Buttons(options).render('.paypal-button-container');
});
});
});
</script>
</script>
@endif

View File

@ -0,0 +1,7 @@
<?php
namespace Webkul\Product\Contracts;
interface ProductVideo
{
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProductVideosTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_videos', function (Blueprint $table) {
$table->increments('id');
$table->string('type')->nullable();
$table->string('path');
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_videos');
}
}

View File

@ -54,7 +54,7 @@ class ProductImage extends AbstractProduct
];
}
if (! $product->parent_id && ! count($images)) {
if (! $product->parent_id && ! count($images) && ! count($product->videos)) {
$images[] = [
'small_image_url' => asset('vendor/webkul/ui/assets/images/product/small-product-placeholder.webp'),
'medium_image_url' => asset('vendor/webkul/ui/assets/images/product/meduim-product-placeholder.webp'),

View File

@ -0,0 +1,36 @@
<?php
namespace Webkul\Product\Helpers;
use Illuminate\Support\Facades\Storage;
class ProductVideo extends AbstractProduct
{
/**
* Retrieve collection of videos
*
* @param \Webkul\Product\Contracts\Product|\Webkul\Product\Contracts\ProductFlat $product
* @return array
*/
public function getVideos($product)
{
if (! $product) {
return [];
}
$videos = [];
foreach ($product->videos as $video) {
if (! Storage::has($video->path)) {
continue;
}
$videos[] = [
'type' => $video->type,
'video_url' => Storage::url($video->path),
];
}
return $videos;
}
}

View File

@ -68,6 +68,7 @@ class ProductForm extends FormRequest
$this->rules = array_merge($product->getTypeInstance()->getTypeValidationRules(), [
'sku' => ['required', 'unique:products,sku,' . $this->id, new \Webkul\Core\Contracts\Validations\Slug],
'images.*' => 'nullable|mimes:bmp,jpeg,jpg,png,webp',
'videos.*' => 'nullable|mimes:mov,mp4|max:2048',
'special_price_from' => 'nullable|date',
'special_price_to' => 'nullable|date|after_or_equal:special_price_from',
'special_price' => ['nullable', new \Webkul\Core\Contracts\Validations\Decimal, 'lt:price'],

View File

@ -143,6 +143,14 @@ class Product extends Model implements ProductContract
return $this->hasMany(ProductImageProxy::modelClass(), 'product_id');
}
/**
* The videos that belong to the product.
*/
public function videos()
{
return $this->hasMany(ProductVideoProxy::modelClass(), 'product_id');
}
/**
* The images that belong to the product.
*/

View File

@ -126,6 +126,14 @@ class ProductFlat extends Model implements ProductFlatContract
->select('product_images.*');
}
/**
* The videos that belong to the product.
*/
public function videos()
{
return $this->product->videos();
}
/**
* Get all of the attributes for the attribute groups.
*/

View File

@ -0,0 +1,64 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Webkul\Product\Contracts\ProductVideo as ProductVideoContract;
class ProductVideo extends Model implements ProductVideoContract
{
public $timestamps = false;
protected $fillable = [
'path',
'product_id',
'type',
];
/**
* Get the product that owns the image.
*/
public function product()
{
return $this->belongsTo(ProductProxy::modelClass());
}
/**
* Get image url for the product image.
*/
public function url()
{
return Storage::url($this->path);
}
/**
* Get image url for the product image.
*/
public function getUrlAttribute()
{
return $this->url();
}
/**
* @param string $key
*
* @return bool
*/
public function isCustomAttribute($attribute)
{
return $this->attribute_family->custom_attributes->pluck('code')->contains($attribute);
}
/**
* @return array
*/
public function toArray()
{
$array = parent::toArray();
$array['url'] = $this->url;
return $array;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Webkul\Product\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ProductVideoProxy extends ModelProxy
{
}

View File

@ -22,5 +22,6 @@ class ModuleServiceProvider extends CoreModuleServiceProvider
\Webkul\Product\Models\ProductBundleOptionTranslation::class,
\Webkul\Product\Models\ProductBundleOptionProduct::class,
\Webkul\Product\Models\ProductCustomerGroupPrice::class,
\Webkul\Product\Models\ProductVideo::class,
];
}

View File

@ -735,6 +735,12 @@ class ProductRepository extends Repository
}
}
if (! in_array('videos', $attributesToSkip)) {
foreach ($originalProduct->videos as $video) {
$copiedProduct->videos()->save($video->replicate());
}
}
if (! in_array('super_attributes', $attributesToSkip)) {
foreach ($originalProduct->super_attributes as $super_attribute) {
$copiedProduct->super_attributes()->save($super_attribute);

View File

@ -0,0 +1,60 @@
<?php
namespace Webkul\Product\Repositories;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Webkul\Core\Eloquent\Repository;
class ProductVideoRepository extends Repository
{
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\Product\Contracts\ProductVideo';
}
/**
* @param array $data
* @param \Webkul\Product\Contracts\Product $product
* @return void
*/
public function uploadVideos($data, $product)
{
$previousVideoIds = $product->videos()->pluck('id');
if (isset($data['videos'])) {
foreach ($data['videos'] as $videoId => $video) {
$file = 'videos.' . $videoId;
$dir = 'product/' . $product->id;
if ($video instanceof UploadedFile) {
if (request()->hasFile($file)) {
$this->create([
'path' => request()->file($file)->store($dir),
'product_id' => $product->id,
'type' => 'video'
]);
}
} else {
if (is_numeric($index = $previousVideoIds->search($videoId))) {
$previousVideoIds->forget($index);
}
}
}
}
foreach ($previousVideoIds as $videoId) {
if ($videoModel = $this->find($videoId)) {
Storage::delete($videoModel->path);
$this->delete($videoId);
}
}
}
}

View File

@ -11,6 +11,7 @@ use Webkul\Product\Repositories\ProductRepository;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Datatypes\CartItemValidationResult;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
@ -47,10 +48,17 @@ abstract class AbstractType
/**
* ProductImageRepository instance
*
* @var \Webkul\Product\Repositories\ProductInventoryRepository
* @var \Webkul\Product\Repositories\productImageRepository
*/
protected $productImageRepository;
/**
* ProductVideoRepository instance
*
* @var \Webkul\Product\Repositories\productVideoRepository
*/
protected $productVideoRepository;
/**
* Product Image helper instance
*
@ -141,6 +149,7 @@ abstract class AbstractType
* @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
*
* @return void
*/
@ -150,7 +159,8 @@ abstract class AbstractType
ProductAttributeValueRepository $attributeValueRepository,
ProductInventoryRepository $productInventoryRepository,
ProductImageRepository $productImageRepository,
ProductImage $productImageHelper
ProductImage $productImageHelper,
ProductVideoRepository $productVideoRepository
) {
$this->attributeRepository = $attributeRepository;
@ -163,6 +173,8 @@ abstract class AbstractType
$this->productImageRepository = $productImageRepository;
$this->productImageHelper = $productImageHelper;
$this->productVideoRepository = $productVideoRepository;
}
/**
@ -271,6 +283,8 @@ abstract class AbstractType
$this->productImageRepository->uploadImages($data, $product);
$this->productVideoRepository->uploadVideos($data, $product);
app('Webkul\Product\Repositories\ProductCustomerGroupPriceRepository')->saveCustomerGroupPrices($data,
$product);
}

View File

@ -8,6 +8,7 @@ use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductBundleOptionRepository;
use Webkul\Product\Repositories\ProductBundleOptionProductRepository;
use Webkul\Product\Helpers\ProductImage;
@ -54,7 +55,8 @@ class Bundle extends AbstractType
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.bundle-items',
'admin::catalog.products.accordians.channels',
'admin::catalog.products.accordians.product-links'
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**
@ -88,6 +90,7 @@ class Bundle extends AbstractType
* @param \Webkul\Product\Repositories\ProductBundleOptionProductRepository $productBundleOptionProductRepository
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\Product\Helpers\BundleOption $bundleOptionHelper
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
* @return void
*/
public function __construct(
@ -99,7 +102,8 @@ class Bundle extends AbstractType
ProductBundleOptionRepository $productBundleOptionRepository,
ProductBundleOptionProductRepository $productBundleOptionProductRepository,
ProductImage $productImageHelper,
BundleOption $bundleOptionHelper
BundleOption $bundleOptionHelper,
ProductVideoRepository $productVideoRepository
)
{
parent::__construct(
@ -108,7 +112,8 @@ class Bundle extends AbstractType
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$productImageHelper
$productImageHelper,
$productVideoRepository
);
$this->productBundleOptionRepository = $productBundleOptionRepository;

View File

@ -29,7 +29,8 @@ class Configurable extends AbstractType
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.variations',
'admin::catalog.products.accordians.channels',
'admin::catalog.products.accordians.product-links'
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**

View File

@ -8,6 +8,7 @@ use Webkul\Product\Repositories\ProductRepository;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Datatypes\CartItemValidationResult;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
@ -47,6 +48,7 @@ class Downloadable extends AbstractType
'admin::catalog.products.accordians.downloadable',
'admin::catalog.products.accordians.channels',
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**
@ -79,6 +81,7 @@ class Downloadable extends AbstractType
* @param \Webkul\Product\Repositories\ProductDownloadableLinkRepository $productDownloadableLinkRepository
* @param \Webkul\Product\Repositories\ProductDownloadableSampleRepository $productDownloadableSampleRepository
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
*
* @return void
*/
@ -90,7 +93,8 @@ class Downloadable extends AbstractType
productImageRepository $productImageRepository,
ProductDownloadableLinkRepository $productDownloadableLinkRepository,
ProductDownloadableSampleRepository $productDownloadableSampleRepository,
ProductImage $productImageHelper
ProductImage $productImageHelper,
ProductVideoRepository $productVideoRepository
)
{
parent::__construct(
@ -99,7 +103,8 @@ class Downloadable extends AbstractType
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$productImageHelper
$productImageHelper,
$productVideoRepository
);
$this->productDownloadableLinkRepository = $productDownloadableLinkRepository;

View File

@ -7,6 +7,7 @@ use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductGroupedProductRepository;
use Webkul\Product\Helpers\ProductImage;
use Webkul\Product\Models\ProductAttributeValue;
@ -38,7 +39,8 @@ class Grouped extends AbstractType
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.grouped-products',
'admin::catalog.products.accordians.channels',
'admin::catalog.products.accordians.product-links'
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**
@ -51,13 +53,14 @@ class Grouped extends AbstractType
/**
* Create a new product type instance.
*
* @param \Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @param \Webkul\Product\Repositories\ProductRepository $productRepository
* @param \Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @param \Webkul\Product\Repositories\ProductRepository $productRepository
* @param \Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository
* @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductGroupedProductRepository $productGroupedProductRepository
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\Product\Helpers\ProductImage $productImageHelper
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
* @return void
*/
public function __construct(
@ -67,7 +70,8 @@ class Grouped extends AbstractType
ProductInventoryRepository $productInventoryRepository,
ProductImageRepository $productImageRepository,
ProductGroupedProductRepository $productGroupedProductRepository,
ProductImage $productImageHelper
ProductImage $productImageHelper,
ProductVideoRepository $productVideoRepository
)
{
parent::__construct(
@ -76,7 +80,8 @@ class Grouped extends AbstractType
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$productImageHelper
$productImageHelper,
$productVideoRepository
);
$this->productGroupedProductRepository = $productGroupedProductRepository;

View File

@ -22,6 +22,7 @@ class Simple extends AbstractType
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.channels',
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**

View File

@ -22,6 +22,7 @@ class Virtual extends AbstractType
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.channels',
'admin::catalog.products.accordians.product-links',
'admin::catalog.products.accordians.videos',
];
/**

View File

@ -1,7 +1,15 @@
@inject ('productImageHelper', 'Webkul\Product\Helpers\ProductImage')
@inject ('productVideoHelper', 'Webkul\Product\Helpers\ProductVideo')
@inject ('wishListHelper', 'Webkul\Customer\Helpers\Wishlist')
<?php $images = $productImageHelper->getGalleryImages($product); ?>
<?php
$images = $productImageHelper->getGalleryImages($product);
$videos = $productVideoHelper->getVideos($product);
$images = array_merge($images, $videos);
?>
{!! view_render_event('bagisto.shop.products.view.gallery.before', ['product' => $product]) !!}
@ -30,7 +38,12 @@
</li>
<li class="thumb-frame" v-for='(thumb, index) in thumbs' @mouseover="changeImage(thumb)" :class="[thumb.large_image_url == currentLargeImageUrl ? 'active' : '']" id="thumb-frame">
<img :src="thumb.small_image_url" alt=""/>
<video v-if="thumb.type == 'video'" width="100%" height="100%" onclick="this.paused ? this.play() : this.pause();">
<source :src="thumb.video_url" type="video/mp4">
{{ __('admin::app.catalog.products.not-support-video') }}
</video>
<img v-else :src="thumb.small_image_url" alt=""/>
</li>
<li class="gallery-control bottom" @click="moveThumbs('bottom')" v-if="(thumbs.length > 4) && this.is_move.down">
@ -39,10 +52,13 @@
</li>
</ul>
<div class="product-hero-image" id="product-hero-image">
<img :src="currentLargeImageUrl" id="pro-img" :data-image="currentOriginalImageUrl" alt=""/>
<video :key="currentVideoUrl" v-if="currentType == 'video'" width="100%" height="100%" controls>
<source :src="currentVideoUrl" :data-image="currentOriginalImageUrl" type="video/mp4">
{{ __('admin::app.catalog.products.not-support-video') }}
</video>
<img v-else :src="currentLargeImageUrl" id="pro-img" :data-image="currentOriginalImageUrl" alt=""/>
@auth('customer')
@php
@ -76,6 +92,10 @@
currentOriginalImageUrl: '',
currentVideoUrl: '',
currentType: '',
counter: {
up: 0,
down: 0,
@ -114,11 +134,19 @@
},
changeImage: function(image) {
this.currentLargeImageUrl = image.large_image_url;
this.currentType = image.type;
this.currentOriginalImageUrl = image.original_image_url;
if (image.type == 'video') {
this.currentVideoUrl = image.video_url;
if ($(window).width() > 580) {
this.currentLargeImageUrl = image.large_image_url = image.video_url;
} else {
this.currentLargeImageUrl = image.large_image_url;
this.currentOriginalImageUrl = image.original_image_url;
}
if ($(window).width() > 580 && image.original_image_url) {
$('img#pro-img').data('zoom-image', image.original_image_url).ezPlus();
}
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"/js/velocity.js": "/js/velocity.js?id=fd91d92b77bfe50ba686",
"/js/velocity.js": "/js/velocity.js?id=a2c7aca94b55b22e8f8e",
"/css/velocity-admin.css": "/css/velocity-admin.css?id=4322502d80a0e4a0affd",
"/css/velocity.css": "/css/velocity.css?id=2df490c7a6700e2c0c3b"
"/css/velocity.css": "/css/velocity.css?id=e41e400c39a3cb5bc551"
}

View File

@ -1,24 +1,38 @@
<template>
<div class="magnifier">
<img
:src="src"
:data-zoom-image="src"
ref="activeProductImage"
<div :class="[!isMobile() ? 'magnifier' : '']">
<video :key=this.activeImageVideoURL v-if="currentType == 'video'" width="100%" height="100%" controls>
<source :src=this.activeImageVideoURL ref="activeProductImage"
id="active-product-image"
class="main-product-image"
alt=""
/>
class="main-product-image" type="video/mp4">
</video>
<img v-else
:src=this.activeImageVideoURL
:data-zoom-image="[!isMobile() ? 'src' : '']"
:class="[!isMobile() ? 'main-product-image' : 'vc-small-product-image']"/>
</div>
</template>
<style lang="scss">
.magnifier {
> img {
max-width: 100%;
min-height: 530px;
max-height: 530px;
}
}
</style>
<script type="text/javascript">
export default {
props: ['src'],
props: ['src', 'type'],
data: function () {
return {
'activeImage': null,
'activeImageElement': null,
activeImageVideoURL: '',
currentType: '',
}
},
@ -26,33 +40,43 @@
/* store image related info in global variables */
this.activeImageElement = this.$refs.activeProductImage;
this.currentType = this.type;
this.activeImageVideoURL = this.src;
/* convert into jQuery object */
this.activeImage = new jQuery.fn.init(this.activeImageElement);
/* initialise zoom */
this.elevateZoom();
this.$root.$on('changeMagnifiedImage', ({smallImageUrl, largeImageUrl}) => {
this.$root.$on('changeMagnifiedImage', ({smallImageUrl, largeImageUrl, currentType}) => {
/* removed old instance */
$('.zoomContainer').remove();
this.activeImage.removeData('elevateZoom');
this.currentType = currentType;
this.activeImageVideoURL = largeImageUrl;
/* update source for images */
this.activeImageElement.src = smallImageUrl;
this.activeImage.data('zoom-image', (largeImageUrl ? largeImageUrl : smallImageUrl));
this.activeImage.attr('src', smallImageUrl);
this.activeImage.data('zoom-image', largeImageUrl);
/* reinitialize zoom */
this.elevateZoom();
});
},
methods: {
'elevateZoom': function () {
this.activeImage.ezPlus({
zoomLevel: 0.5,
cursor: 'pointer',
scrollZoom: true,
zoomWindowWidth: 400,
zoomWindowHeight: 400,
zoomWindowWidth: 250,
zoomWindowHeight: 250,
});
},
}

View File

@ -123,7 +123,7 @@
},
checkMinimumOrder: function (e) {
if (! this.isMinimumOrderCompleted) {
if (! Boolean(this.isMinimumOrderCompleted)) {
e.preventDefault();
window.showAlert(`alert-warning`, 'Warning', this.minimumOrderMessage);
}

View File

@ -5,7 +5,7 @@
<template v-else-if="productCollections.length > 0">
<card-list-header
:heading="isCategory ? categoryDetails.name : productTitle"
:view-all="isCategory ? `${this.baseUrl}/${categoryDetails.slug}` : ''">
:view-all="isCategory ? `${this.baseUrl}/${categoryDetails.url_path}` : ''">
</card-list-header>
<div class="row" :class="localeDirection">

View File

@ -4,19 +4,6 @@
padding-left: 0 !important;
padding-right: 0 !important;
.magnifier {
> img {
max-width: 100%;
min-height: 450px;
max-height: 530px;
}
}
img {
// width: 100%;
// height: 100%;
}
.right {
@extend .fs16;

View File

@ -9,7 +9,7 @@
checkout-text="{{ __('shop::app.minicart.checkout') }}"
checkout-url="{{ route('shop.checkout.onepage.index') }}"
subtotal-text="{{ __('shop::app.checkout.cart.cart-subtotal') }}"
is-minimum-order-completed="{{ Cart::checkMinimumOrder() }}"
is-minimum-order-completed="{{ (bool) Cart::checkMinimumOrder() }}"
minimum-order-message="{{ __('shop::app.checkout.cart.minimum-order-message', ['amount' => core()->currency($minimumOrderAmount)]) }}">
</mini-cart>
</div>

View File

@ -1,75 +1,89 @@
@inject ('productImageHelper', 'Webkul\Product\Helpers\ProductImage')
@inject ('productVideoHelper', 'Webkul\Product\Helpers\ProductVideo')
@inject ('wishListHelper', 'Webkul\Customer\Helpers\Wishlist')
@php
$images = $productImageHelper->getGalleryImages($product);
$videos = $productVideoHelper->getVideos($product);
if (! count($images)) {
$images[0] = $productImageHelper->getProductBaseImage($product);
$videoData = $imageData = [];
foreach ($videos as $key => $video) {
$videoData[$key]['type'] = $video['type'];
$videoData[$key]['large_image_url'] = $videoData[$key]['small_image_url']= $videoData[$key]['medium_image_url']= $videoData[$key]['original_image_url'] = $video['video_url'];
}
foreach ($images as $key => $image) {
$imageData[$key]['type'] = '';
$imageData[$key]['large_image_url'] = $image['large_image_url'];
$imageData[$key]['small_image_url'] = $image['small_image_url'];
$imageData[$key]['medium_image_url'] = $image['medium_image_url'];
$imageData[$key]['original_image_url'] = $image['original_image_url'];
}
$images = array_merge($imageData, $videoData);
@endphp
{!! view_render_event('bagisto.shop.products.view.gallery.before', ['product' => $product]) !!}
<div class="product-image-group">
<div class="row">
<div class="col-12">
<magnify-image src="{{ $images[0]['large_image_url'] }}" v-if="!isMobile()">
</magnify-image>
<div class="row col-12">
<magnify-image src="{{ $images[0]['large_image_url'] }}" type="{{ $images[0]['type'] }}">
</magnify-image>
</div>
<img
v-else
class="vc-small-product-image"
src="{{ $images[0]['large_image_url'] }}" alt="" />
</div>
</div>
<div class="row">
<div class="col-12">
<product-gallery></product-gallery>
</div>
<div class="row col-12">
<product-gallery></product-gallery>
</div>
</div>
{!! view_render_event('bagisto.shop.products.view.gallery.after', ['product' => $product]) !!}
<script type="text/x-template" id="product-gallery-template">
<ul class="thumb-list col-12 row ltr" type="none">
<li class="arrow left" @click="scroll('prev')" v-if="thumbs.length > 4">
<i class="rango-arrow-left fs24"></i>
</li>
<carousel-component
slides-per-page="4"
:id="galleryCarouselId"
pagination-enabled="hide"
navigation-enabled="hide"
add-class="product-gallery"
:slides-count="thumbs.length">
<slide :slot="`slide-${index}`" v-for="(thumb, index) in thumbs">
<li
@mouseover="changeImage({
largeImageUrl: thumb.large_image_url,
originalImageUrl: thumb.original_image_url,
currentType: thumb.type
})"
:class="`thumb-frame ${index + 1 == 4 ? '' : 'mr5'} ${thumb.large_image_url == currentLargeImageUrl ? 'active' : ''}`"
>
<video v-if="thumb.type == 'video'" width="110" height="110" controls>
<source :src="thumb.small_image_url" type="video/mp4">
{{ __('admin::app.catalog.products.not-support-video') }}
</video>
<div v-else
class="bg-image"
:style="`background-image: url(${thumb.small_image_url})`">
</div>
</li>
</slide>
</carousel-component>
<li class="arrow right" @click="scroll('next')" v-if="thumbs.length > 4">
<i class="rango-arrow-right fs24"></i>
</li>
</ul>
</script>
@push('scripts')
<script type="text/x-template" id="product-gallery-template">
<ul class="thumb-list col-12 row ltr" type="none">
<li class="arrow left" @click="scroll('prev')" v-if="thumbs.length > 4">
<i class="rango-arrow-left fs24"></i>
</li>
<carousel-component
slides-per-page="4"
:id="galleryCarouselId"
pagination-enabled="hide"
navigation-enabled="hide"
add-class="product-gallery"
:slides-count="thumbs.length">
<slide :slot="`slide-${index}`" v-for="(thumb, index) in thumbs">
<li
@mouseover="changeImage({
largeImageUrl: thumb.large_image_url,
originalImageUrl: thumb.original_image_url,
})"
:class="`thumb-frame ${index + 1 == 4 ? '' : 'mr5'} ${thumb.large_image_url == currentLargeImageUrl ? 'active' : ''}`"
>
<div
class="bg-image"
:style="`background-image: url(${thumb.small_image_url})`">
</div>
</li>
</slide>
</carousel-component>
<li class="arrow right" @click="scroll('next')" v-if="thumbs.length > 4">
<i class="rango-arrow-right fs24"></i>
</li>
</ul>
</script>
<script type="text/javascript">
(() => {
var galleryImages = @json($images);
@ -83,6 +97,7 @@
galleryCarouselId: 'product-gallery-carousel',
currentLargeImageUrl: '',
currentOriginalImageUrl: '',
currentType: '',
counter: {
up: 0,
down: 0,
@ -96,6 +111,7 @@
this.changeImage({
largeImageUrl: this.images[0]['large_image_url'],
originalImageUrl: this.images[0]['original_image_url'],
currentType: this.images[0]['type']
})
}
@ -107,11 +123,12 @@
this.changeImage({
largeImageUrl: this.images[0]['large_image_url'],
originalImageUrl: this.images[0]['original_image_url'],
currentType: this.images[0]['type']
});
eventBus.$on('configurable-variant-update-images-event', this.updateImages);
this.prepareThumbs()
this.prepareThumbs();
},
methods: {
@ -127,13 +144,17 @@
});
},
changeImage: function({largeImageUrl, originalImageUrl}) {
changeImage: function({largeImageUrl, originalImageUrl, currentType}) {
this.currentLargeImageUrl = largeImageUrl;
this.currentOriginalImageUrl = originalImageUrl;
this.currentType = currentType;
this.$root.$emit('changeMagnifiedImage', {
smallImageUrl: this.currentOriginalImageUrl
smallImageUrl: this.currentOriginalImageUrl,
largeImageUrl: this.currentLargeImageUrl,
currentType : this.currentType
});
let productImage = $('.vc-small-product-image');