Merge branch 'bagisto/master' into feature/update-npm-assets

This commit is contained in:
peternuernberger 2019-12-18 09:41:40 +01:00
commit d92bb0ea72
116 changed files with 1632 additions and 612 deletions

View File

@ -13,6 +13,7 @@ DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
DB_PREFIX=
BROADCAST_DRIVER=log
CACHE_DRIVER=file

45
.env.testing Normal file
View File

@ -0,0 +1,45 @@
APP_NAME=Bagisto
APP_ENV=local
APP_VERSION=0.1.8
APP_KEY=base64:NFtGjjFAqET6RlX3PVC/gFpzHb4jK1OxDc3cuU5Asz4=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=bagisto_test
DB_USERNAME=root
DB_PASSWORD=root
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=20
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
SHOP_MAIL_FROM=
ADMIN_MAIL_TO=
fixer_api_key=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

11
codeception.yml Normal file
View File

@ -0,0 +1,11 @@
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed

View File

@ -36,6 +36,7 @@
},
"require-dev": {
"codeception/codeception": "3.1.*",
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
@ -125,6 +126,12 @@
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
],
"test": [
"set -e",
"@php artisan migrate:fresh --env=testing",
"vendor/bin/codecept run unit",
"vendor/bin/codecept run functional"
]
},
"config": {
@ -132,5 +139,6 @@
"sort-packages": true,
"optimize-autoloader": true
},
"minimum-stability": "dev"
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -36,7 +36,7 @@ return [
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'prefix' => env('DB_PREFIX'),
],
'mysql' => [
@ -49,7 +49,7 @@ return [
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix' => env('DB_PREFIX'),
'strict' => false,
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
],
@ -62,7 +62,7 @@ return [
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix' => env('DB_PREFIX'),
'schema' => 'public',
'sslmode' => 'prefer',
],

View File

@ -60,6 +60,20 @@ return [
'name' => env('MAIL_FROM_NAME')
],
/*
|--------------------------------------------------------------------------
| Global "Admin" Address
|--------------------------------------------------------------------------
|
| General admin related admins, such as order notifications.
|
*/
'admin' => [
'address' => env('ADMIN_MAIL_TO'),
'name' => env('ADMIN_MAIL_NAME', 'Admin')
],
/*
|--------------------------------------------------------------------------
| E-Mail Encryption Protocol

View File

@ -52,7 +52,7 @@ class AddressDataGrid extends DataGrid
$queryBuilder = DB::table('customer_addresses as ca')
->leftJoin('countries', 'ca.country', '=', 'countries.code')
->leftJoin('customers as c', 'ca.customer_id', '=', 'c.id')
->addSelect('ca.id as address_id', 'ca.address1', 'ca.country', DB::raw('countries.name as country_name'), 'ca.state', 'ca.city', 'ca.postcode', 'ca.phone', 'ca.default_address')
->addSelect('ca.id as address_id', 'ca.address1', 'ca.country', DB::raw(''.DB::getTablePrefix().'countries.name as country_name'), 'ca.state', 'ca.city', 'ca.postcode', 'ca.phone', 'ca.default_address')
->where('c.id', $customer->id);
$queryBuilder = $queryBuilder->leftJoin('country_states', function($qb) {
@ -62,13 +62,13 @@ class AddressDataGrid extends DataGrid
$queryBuilder
->groupBy('ca.id')
->addSelect(DB::raw('country_states.default_name as state_name'));
->addSelect(DB::raw(''.DB::getTablePrefix().'country_states.default_name as state_name'));
$this->addFilter('address_id', 'ca.id');
$this->addFilter('address1', 'ca.address1');
$this->addFilter('city', 'ca.city');
$this->addFilter('state_name', DB::raw('country_states.default_name'));
$this->addFilter('country_name', DB::raw('countries.name'));
$this->addFilter('state_name', DB::raw(''.DB::getTablePrefix().'country_states.default_name'));
$this->addFilter('country_name', DB::raw(''.DB::getTablePrefix().'countries.name'));
$this->addFilter('postcode', 'ca.postcode');
$this->addFilter('default_address', 'ca.default_address');

View File

@ -21,7 +21,7 @@ class CategoryDataGrid extends DataGrid
{
$queryBuilder = DB::table('categories as cat')
->select('cat.id as category_id', 'ct.name', 'cat.position', 'cat.status', 'ct.locale',
DB::raw('COUNT(DISTINCT pc.product_id) as count'))
DB::raw('COUNT(DISTINCT '.DB::getTablePrefix().'pc.product_id) as count'))
->leftJoin('category_translations as ct', function($leftJoin) {
$leftJoin->on('cat.id', '=', 'ct.category_id')
->where('ct.locale', app()->getLocale());

View File

@ -25,10 +25,10 @@ class CustomerDataGrid extends DataGrid
$queryBuilder = DB::table('customers')
->leftJoin('customer_groups', 'customers.customer_group_id', '=', 'customer_groups.id')
->addSelect('customers.id as customer_id', 'customers.email', 'customer_groups.name', 'customers.phone', 'customers.gender', 'status')
->addSelect(DB::raw('CONCAT(customers.first_name, " ", customers.last_name) as full_name'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'customers.first_name, " ", '.DB::getTablePrefix().'customers.last_name) as full_name'));
$this->addFilter('customer_id', 'customers.id');
$this->addFilter('full_name', DB::raw('CONCAT(customers.first_name, " ", customers.last_name)'));
$this->addFilter('full_name', DB::raw('CONCAT('.DB::getTablePrefix().'customers.first_name, " ", '.DB::getTablePrefix().'customers.last_name)'));
$this->addFilter('phone', 'customers.phone');
$this->addFilter('gender', 'customers.gender');

View File

@ -29,11 +29,11 @@ class OrderDataGrid extends DataGrid
->where('order_address_billing.address_type', 'billing');
})
->addSelect('orders.id','orders.increment_id', 'orders.base_sub_total', 'orders.base_grand_total', 'orders.created_at', 'channel_name', 'status')
->addSelect(DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name) as shipped_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
$this->addFilter('billed_to', DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name)'));
$this->addFilter('billed_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->addFilter('increment_id', 'orders.increment_id');
$this->addFilter('created_at', 'orders.created_at');

View File

@ -26,9 +26,9 @@ class OrderRefundDataGrid extends DataGrid
$leftJoin->on('order_address_billing.order_id', '=', 'orders.id')
->where('order_address_billing.address_type', 'billing');
})
->addSelect(DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name) as billed_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name) as billed_to'));
$this->addFilter('billed_to', DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name)'));
$this->addFilter('billed_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name)'));
$this->addFilter('id', 'refunds.id');
$this->addFilter('increment_id', 'orders.increment_id');
$this->addFilter('state', 'refunds.state');

View File

@ -27,7 +27,7 @@ class OrderShipmentsDataGrid extends DataGrid
->leftJoin('orders as ors', 'shipments.order_id', '=', 'ors.id')
->leftJoin('inventory_sources as is', 'shipments.inventory_source_id', '=', 'is.id')
->select('shipments.id as shipment_id', 'ors.increment_id as shipment_order_id', 'shipments.total_qty as shipment_total_qty', 'is.name as inventory_source_name', 'ors.created_at as order_date', 'shipments.created_at as shipment_created_at')
->addSelect(DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name) as shipped_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
$this->addFilter('shipment_id', 'shipments.id');
$this->addFilter('shipment_order_id', 'ors.increment_id');
@ -35,7 +35,7 @@ class OrderShipmentsDataGrid extends DataGrid
$this->addFilter('inventory_source_name', 'is.name');
$this->addFilter('order_date', 'ors.created_at');
$this->addFilter('shipment_created_at', 'shipments.created_at');
$this->addFilter('shipped_to', DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name)'));
$this->addFilter('shipped_to', DB::raw(''.DB::getTablePrefix().'CONCAT(order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->setQueryBuilder($queryBuilder);
}

View File

@ -17,7 +17,7 @@ class ProductDataGrid extends DataGrid
protected $index = 'product_id';
protected $itemsPerPage = 20;
protected $itemsPerPage = 10;
public function prepareQueryBuilder()
{
@ -25,7 +25,7 @@ class ProductDataGrid extends DataGrid
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->leftJoin('attribute_families', 'products.attribute_family_id', '=', 'attribute_families.id')
->leftJoin('product_inventories', 'product_flat.product_id', '=', 'product_inventories.product_id')
->select('product_flat.product_id as product_id', 'product_flat.sku as product_sku', 'product_flat.name as product_name', 'products.type as product_type', 'product_flat.status', 'product_flat.price', 'attribute_families.name as attribute_family', DB::raw('SUM(product_inventories.qty) as quantity'))
->select('product_flat.product_id as product_id', 'product_flat.sku as product_sku', 'product_flat.name as product_name', 'products.type as product_type', 'product_flat.status', 'product_flat.price', 'attribute_families.name as attribute_family', DB::raw('SUM('.DB::getTablePrefix().'product_inventories.qty) as quantity'))
->where('channel', core()->getCurrentChannelCode())
->where('locale', app()->getLocale())
->groupBy('product_flat.product_id');

View File

@ -120,6 +120,10 @@ class ConfigurationController extends Controller
{
Event::fire('core.configuration.save.before');
$this->validate(request(), [
'general.design.admin_logo.logo_image' => 'mimes:jpeg,bmp,png,jpg'
]);
$this->coreConfigRepository->create(request()->all());
Event::fire('core.configuration.save.after');

View File

@ -186,7 +186,7 @@ class DashboardController extends Controller
->where('order_items.created_at', '>=', $this->startDate)
->where('order_items.created_at', '<=', $this->endDate)
->addSelect(DB::raw('SUM(qty_invoiced - qty_refunded) as total_qty_invoiced'))
->addSelect(DB::raw('COUNT(products.id) as total_products'))
->addSelect(DB::raw('COUNT('.DB::getTablePrefix().'products.id) as total_products'))
->addSelect('order_items.id', 'categories.id as category_id', 'category_translations.name')
->groupBy('categories.id')
->havingRaw('SUM(qty_invoiced - qty_refunded) > 0')

View File

@ -42,7 +42,7 @@ class NewAdminNotification extends Mailable
*/
public function build()
{
return $this->to(env('ADMIN_MAIL_TO'))
return $this->to(config('mail.admin.address'))
->subject(trans('shop::app.mail.order.subject'))
->view('shop::emails.sales.new-admin-order');
}

View File

@ -45,7 +45,7 @@ class NewInvoiceNotification extends Mailable
$order = $this->invoice->order;
return $this->to($order->customer_email, $order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.invoice.subject', ['order_id' => $order->increment_id]))
->view('shop::emails.sales.new-invoice');
}

View File

@ -42,7 +42,7 @@ class NewOrderNotification extends Mailable
public function build()
{
return $this->to($this->order->customer_email, $this->order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.order.subject'))
->view('shop::emails.sales.new-order');
}

View File

@ -45,7 +45,7 @@ class NewRefundNotification extends Mailable
$order = $this->refund->order;
return $this->to($order->customer_email, $order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.refund.subject', ['order_id' => $order->increment_id]))
->view('shop::emails.sales.new-refund');
}

View File

@ -45,7 +45,7 @@ class NewShipmentNotification extends Mailable
$order = $this->shipment->order;
return $this->to($order->customer_email, $order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.shipment.subject', ['order_id' => $order->increment_id]))
->view('shop::emails.sales.new-shipment');
}

View File

@ -729,7 +729,6 @@ return [
],
'development' => [
'title' => 'تطوير',
'webconsole' => 'وحدة تحكم الويب',
]
],
@ -914,6 +913,7 @@ return [
'order-number-length' => 'Order Number Length',
'order-number-suffix' => 'Order Number Suffix',
'default' => 'Default',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -838,7 +838,6 @@ return [
],
'development' => [
'title' => 'Development',
'webconsole' => 'Web Console',
]
],
@ -1234,6 +1233,7 @@ return [
'order-number-length' => 'Order Number Length',
'order-number-suffix' => 'Order Number Suffix',
'default' => 'Default',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -764,7 +764,6 @@ return [
],
'development' => [
'title' => 'توسعه',
'webconsole' => 'کنسول وب',
]
],
@ -1052,7 +1051,8 @@ return [
'logo-image' => 'تصویر لوگو',
'credit-max' => 'اعتبار مشتری حداکثر',
'credit-max-value' => 'حداکثر میزان اعتبار',
'use-credit-max' => 'استفاده از حداکثر اعتبار'
'use-credit-max' => 'استفاده از حداکثر اعتبار',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -755,7 +755,6 @@ return [
],
'development' => [
'title' => 'Desenvolvimento',
'webconsole' => 'Console da Web',
]
],
'customers' => [
@ -1036,6 +1035,7 @@ return [
'order-number-prefix' => 'Order Number Prefix',
'order-number-length' => 'Order Number Length',
'order-number-suffix' => 'Order Number Suffix',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -64,7 +64,7 @@
</select>
@if ($familyId)
<input type="hidden" name="type" value="configurable"/>
<input type="hidden" name="type" value="{{ app('request')->input('type') }}"/>
@endif
<span class="control-error" v-if="errors.has('type')">@{{ errors.first('type') }}</span>
</div>

View File

@ -13,16 +13,7 @@
</div>
<div class="page-content">
<a href="{{ route('admin.development.webconsole') }}" target="_blank">
<div class="rad-info-box">
<p>
<span class="heading">{{ __('admin::app.settings.development.webconsole') }}</span>
</p>
<p>
<i class="icon cms-icon"></i>
</p>
</div>
</a>
</div>
</div>
@stop

View File

@ -40,7 +40,9 @@ use Webkul\Core\Repositories\LocaleRepository as Locale;
/**
* Pass the class instance through admin middleware
*/
$this->middleware('auth:admin');
// $this->middleware('auth:admin');
$this->middleware('admin');
/**
* Channel repository instance

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Category\Models\Category;
$factory->define(Category::class, function (Faker $faker, array $attributes) {
return [
'status' => 1,
'position' => $faker->randomDigit,
'parent_id' => 1,
];
});
$factory->state(Category::class, 'inactive', [
'status' => 0,
]);

View File

@ -8,6 +8,13 @@ use Illuminate\Support\Facades\Storage;
use Webkul\Category\Contracts\Category as CategoryContract;
use Webkul\Attribute\Models\AttributeProxy;
/**
* Class Category
*
* @package Webkul\Category\Models
*
* @property-read string $url_path maintained by database triggers
*/
class Category extends TranslatableModel implements CategoryContract
{
use NodeTrait;

View File

@ -5,6 +5,13 @@ namespace Webkul\Category\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Category\Contracts\CategoryTranslation as CategoryTranslationContract;
/**
* Class CategoryTranslation
*
* @package Webkul\Category\Models
*
* @property-read string $url_path maintained by database triggers
*/
class CategoryTranslation extends Model implements CategoryTranslationContract
{
public $timestamps = false;

View File

@ -3,6 +3,8 @@
namespace Webkul\Category\Observers;
use Illuminate\Support\Facades\Storage;
use Webkul\Category\Models\Category;
use Carbon\Carbon;
class CategoryObserver
{
@ -16,4 +18,16 @@ class CategoryObserver
{
Storage::deleteDirectory('category/' . $category->id);
}
/**
* Handle the Category "saved" event.
*
* @param Category $category
*/
public function saved($category)
{
foreach ($category->children as $child) {
$child->touch();
}
}
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Category\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Webkul\Category\Models\CategoryProxy;
use Webkul\Category\Observers\CategoryObserver;
@ -18,6 +19,8 @@ class CategoryServiceProvider extends ServiceProvider
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
CategoryProxy::observe(CategoryObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -29,4 +32,15 @@ class CategoryServiceProvider extends ServiceProvider
{
}
/**
* Register factories.
*
* @param string $path
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -151,6 +151,16 @@ class CategoryRepository extends Repository
);
}
/**
* @param string $urlPath
*
* @return mixed
*/
public function findByPath(string $urlPath)
{
return $this->model->whereTranslation('url_path', $urlPath)->first();
}
/**
* @param array $data
* @param $id

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Core\Models\Locale;
$factory->define(Locale::class, function (Faker $faker, array $attributes) {
return [
'code' => $faker->languageCode,
'name' => $faker->country,
'direction' => 'ltr',
];
});
$factory->state(Category::class, 'rtl', [
'direction' => 'rtl',
]);

View File

@ -2,6 +2,7 @@
namespace Webkul\Core\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\AliasLoader;
@ -36,6 +37,8 @@ class CoreServiceProvider extends ServiceProvider
]);
SliderProxy::observe(SliderObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -61,4 +64,15 @@ class CoreServiceProvider extends ServiceProvider
return app()->make(Core::class);
});
}
/**
* Register factories.
*
* @param string $path
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -64,10 +64,7 @@ class WishlistController extends Controller
*/
public function index()
{
$wishlistItems = $this->wishlistRepository->findWhere([
'channel_id' => core()->getCurrentChannel()->id,
'customer_id' => auth()->guard('customer')->user()->id]
);
$wishlistItems = $this->wishlistRepository->getCustomerWhishlist();
return view($this->_config['view'])->with('items', $wishlistItems);
}
@ -166,14 +163,14 @@ class WishlistController extends Controller
} else {
session()->flash('info', trans('shop::app.wishlist.option-missing'));
return redirect()->route('shop.products.index', $wishlistItem->product->url_key);
return redirect()->route('shop.productOrCategory.index', $wishlistItem->product->url_key);
}
return redirect()->back();
} catch (\Exception $e) {
session()->flash('warning', $e->getMessage());
return redirect()->route('shop.products.index', ['slug' => $wishlistItem->product->url_key]);
return redirect()->route('shop.productOrCategory.index', ['slug' => $wishlistItem->product->url_key]);
}
}

View File

@ -31,7 +31,7 @@ class RegistrationEmail extends Mailable
public function build()
{
return $this->to($this->data['email'])
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.customer.registration.customer-registration'))
->view('shop::emails.customer.registration')->with('data', $this->data);
}

View File

@ -31,7 +31,7 @@ class VerificationEmail extends Mailable
public function build()
{
return $this->to($this->verificationData['email'])
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.customer.verification.subject'))
->view('shop::emails.customer.verification-email')->with('data', ['email' => $this->verificationData['email'], 'token' => $this->verificationData['token']]);
}

View File

@ -21,7 +21,7 @@ class CustomerResetPassword extends ResetPassword
}
return (new MailMessage)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(__('shop::app.mail.forget-password.subject') )
->view('shop::emails.customer.forget-password', [
'user_name' => $notifiable->name,

View File

@ -62,7 +62,6 @@ class WishlistRepository extends Repository
return $this->model->find($id)->item_wishlist;
}
/**
* get customer wishlist Items.
*

View File

@ -159,6 +159,22 @@ return [
'validation' => 'required',
'channel_based' => false,
'locale_based' => true
], [
'name' => 'sandbox',
'title' => 'admin::app.admin.system.sandbox',
'type' => 'select',
'options' => [
[
'title' => 'Active',
'value' => true
], [
'title' => 'Inactive',
'value' => false
]
],
'validation' => 'required',
'channel_based' => false,
'locale_based' => true
], [
'name' => 'sort',
'title' => 'admin::app.admin.system.sort_order',

View File

@ -40,7 +40,7 @@ class Review extends AbstractProduct
if(array_key_exists($product->id, $avgRating))
return $avgRating[$product->id];
return $avgRating[$product->id] = number_format(round($product->reviews()->where('status', 'approved')->average('rating'), 2), 1);
return $avgRating[$product->id] = number_format(round($product->reviews()->where('status', 'approved')->avg('rating'), 2), 1);
}
/**

View File

@ -227,6 +227,22 @@ class ProductRepository extends Repository
return $product;
}
/**
* Retrieve product from slug without throwing an exception (might return null)
*
* @param $slug
*
* @return mixed
*/
public function findBySlug($slug)
{
return app('Webkul\Product\Repositories\ProductFlatRepository')->findOneWhere([
'url_key' => $slug,
'locale' => app()->getLocale(),
'channel' => core()->getCurrentChannelCode(),
]);
}
/**
* Returns newly added product
*

View File

@ -143,9 +143,15 @@ class Configurable extends AbstractType
];
}
$typeOfVariants = 'simple';
$productInstance = app(config('product_types.' . $product->type . '.class'));
if (isset($productInstance->variantsType) && ! in_array($productInstance->variantsType , ['bundle', 'configurable', 'grouped'])) {
$typeOfVariants = $productInstance->variantsType;
}
$variant = $this->productRepository->getModel()->create([
'parent_id' => $product->id,
'type' => 'simple',
'type' => $typeOfVariants,
'attribute_family_id' => $product->attribute_family_id,
'sku' => $data['sku'],
]);

View File

@ -55,7 +55,7 @@ class DownloadableLinkPurchasedRepository extends Repository
*/
public function saveLinks($orderItem)
{
if ($orderItem->type != 'downloadable' || ! isset($orderItem->additional['links']))
if (stristr($orderItem->type,'downloadable') === false || ! isset($orderItem->additional['links']))
return;
foreach ($orderItem->additional['links'] as $linkId) {

View File

@ -198,22 +198,20 @@ class OrderRepository extends Repository
{
$config = new CoreConfig();
foreach ([
'Prefix' => 'prefix',
foreach ([ 'Prefix' => 'prefix',
'Length' => 'length',
'Suffix' => 'suffix',
] as $varSuffix => $confKey) {
'Suffix' => 'suffix', ] as
$varSuffix => $confKey)
{
$var = "invoiceNumber{$varSuffix}";
$$var = $config
->where('code', '=', "sales.orderSettings.order_number.order_number_{$confKey}")
->first() ?: false;
$$var = $config->where('code', '=', "sales.orderSettings.order_number.order_number_{$confKey}")->first() ?: false;
}
$lastOrder = $this->model->orderBy('id', 'desc')->limit(1)->first();
$lastId = $lastOrder ? $lastOrder->id : 0;
if ($invoiceNumberLength && ($invoiceNumberPrefix || $invoiceNumberSuffix)) {
$invoiceNumber = $invoiceNumberPrefix . sprintf("%0{$invoiceNumberLength}d", 0) . ($lastId + 1) . $invoiceNumberSuffix;
$invoiceNumber = ($invoiceNumberPrefix->value) . sprintf("%0{$invoiceNumberLength->value}d", 0) . ($lastId + 1) . ($invoiceNumberSuffix->value);
} else {
$invoiceNumber = $lastId + 1;
}

View File

@ -22,7 +22,7 @@ class DownloadableProductDataGrid extends DataGrid
$queryBuilder = DB::table('downloadable_link_purchased')
->leftJoin('orders', 'downloadable_link_purchased.order_id', '=', 'orders.id')
->addSelect('downloadable_link_purchased.*', 'orders.increment_id')
->addSelect(DB::raw('(downloadable_link_purchased.download_bought - downloadable_link_purchased.download_used) as remaining_downloads'))
->addSelect(DB::raw('('.DB::getTablePrefix().'downloadable_link_purchased.download_bought - '.DB::getTablePrefix().'downloadable_link_purchased.download_used) as remaining_downloads'))
->where('downloadable_link_purchased.customer_id', auth()->guard('customer')->user()->id);
$this->addFilter('status', 'downloadable_link_purchased.status');

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddColumnUrlPathToCategoryTranslations extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('category_translations', function (Blueprint $table) {
$table->string('url_path', 2048)
->comment('maintained by database triggers');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('category_translations', function (Blueprint $table) {
$table->dropColumn('url_path');
});
}
}

View File

@ -0,0 +1,68 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
class AddStoredFunctionToGetUrlPathOfCategory extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$functionSQL = <<< SQL
DROP FUNCTION IF EXISTS `get_url_path_of_category`;
CREATE FUNCTION get_url_path_of_category(
categoryId INT,
localeCode VARCHAR(255)
)
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE urlPath VARCHAR(255);
-- Category with id 1 is root by default
IF categoryId <> 1
THEN
SELECT
GROUP_CONCAT(parent_translations.slug SEPARATOR '/') INTO urlPath
FROM
categories AS node,
categories AS parent
JOIN category_translations AS parent_translations ON parent.id = parent_translations.category_id
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
AND node.id = categoryId
AND parent.id <> 1
AND parent_translations.locale = localeCode
GROUP BY
node.id;
IF urlPath IS NULL
THEN
SET urlPath = (SELECT slug FROM category_translations WHERE category_translations.category_id = categoryId);
END IF;
ELSE
SET urlPath = '';
END IF;
RETURN urlPath;
END;
SQL;
DB::unprepared($functionSQL);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::unprepared('DROP FUNCTION IF EXISTS `get_url_path_of_category`;');
}
}

View File

@ -0,0 +1,98 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
class AddTriggerToCategoryTranslations extends Migration
{
private const TRIGGER_NAME_INSERT = 'trig_category_translations_insert';
private const TRIGGER_NAME_UPDATE = 'trig_category_translations_update';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$triggerBody = $this->getTriggerBody();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE INSERT ON category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE UPDATE ON category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf($insertTrigger, self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
DB::unprepared(sprintf($updateTrigger, self::TRIGGER_NAME_UPDATE));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
}
/**
* Returns trigger body as string
*
* @return string
*/
private function getTriggerBody()
{
return <<<SQL
DECLARE parentUrlPath varchar(255);
DECLARE urlPath varchar(255);
-- Category with id 1 is root by default
IF NEW.category_id <> 1
THEN
SELECT
GROUP_CONCAT(parent_translations.slug SEPARATOR '/') INTO parentUrlPath
FROM
categories AS node,
categories AS parent
JOIN category_translations AS parent_translations ON parent.id = parent_translations.category_id
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
AND node.id = (SELECT parent_id FROM categories WHERE id = NEW.category_id)
AND parent.id <> 1
AND parent_translations.locale = NEW.locale
GROUP BY
node.id;
IF parentUrlPath IS NULL
THEN
SET urlPath = NEW.slug;
ELSE
SET urlPath = concat(parentUrlPath, '/', NEW.slug);
END IF;
SET NEW.url_path = urlPath;
END IF;
SQL;
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
use Webkul\Category\Models\CategoryTranslation;
class AddUrlPathToExistingCategoryTranslations extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$sqlStoredFunction = <<< SQL
SELECT get_url_path_of_category(:category_id, :locale_code) AS url_path;
SQL;
$categoryTranslationsTableName = app(CategoryTranslation::class)->getTable();
foreach (DB::table($categoryTranslationsTableName)->get() as $categoryTranslation) {
$urlPathQueryResult = DB::selectOne($sqlStoredFunction, [
'category_id' => $categoryTranslation->category_id,
'locale_code' => $categoryTranslation->locale,
]);
$url_path = $urlPathQueryResult ? $urlPathQueryResult->url_path : '';
DB::table($categoryTranslationsTableName)
->where('id', $categoryTranslation->id)
->update(['url_path' => $url_path]);
}
}
}

View File

@ -0,0 +1,99 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
class AddTriggerToCategories extends Migration
{
private const TRIGGER_NAME_INSERT = 'trig_categories_insert';
private const TRIGGER_NAME_UPDATE = 'trig_categories_update';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$triggerBody = $this->getTriggerBody();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
AFTER INSERT ON categories
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
AFTER UPDATE ON categories
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf($insertTrigger, self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
DB::unprepared(sprintf($updateTrigger, self::TRIGGER_NAME_UPDATE));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
}
/**
* Returns trigger body as string
*
* @return string
*/
private function getTriggerBody(): string
{
return <<< SQL
DECLARE urlPath VARCHAR(255);
DECLARE localeCode VARCHAR(255);
DECLARE done INT;
DECLARE curs CURSOR FOR (SELECT category_translations.locale
FROM category_translations
WHERE category_id = NEW.id);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
IF EXISTS (
SELECT *
FROM category_translations
WHERE category_id = NEW.id
)
THEN
OPEN curs;
SET done = 0;
REPEAT
FETCH curs INTO localeCode;
SELECT get_url_path_of_category(NEW.id, localeCode) INTO urlPath;
UPDATE category_translations
SET url_path = urlPath
WHERE category_translations.category_id = NEW.id;
UNTIL done END REPEAT;
CLOSE curs;
END IF;
SQL;
}
}

View File

@ -96,7 +96,7 @@ class CartController extends Controller
$product = $this->productRepository->find($id);
return redirect()->route('shop.products.index', ['slug' => $product->url_key]);
return redirect()->route('shop.productOrCategory.index', ['slug' => $product->url_key]);
}
return redirect()->back();

View File

@ -38,17 +38,4 @@ class CategoryController extends Controller
$this->_config = request('_config');
}
/**
* Display a listing of the resource.
*
* @param string $slug
* @return \Illuminate\View\View
*/
public function index($slug)
{
$category = $this->categoryRepository->findBySlugOrFail($slug);
return view($this->_config['view'], compact('category'));
}
}

View File

@ -79,21 +79,6 @@ class ProductController extends Controller
$this->_config = request('_config');
}
/**
* Display a listing of the resource.
*
* @param string $slug
* @return \Illuminate\View\View
*/
public function index($slug)
{
$product = $this->productRepository->findBySlugOrFail($slug);
$customer = auth()->guard('customer')->user();
return view($this->_config['view'], compact('product', 'customer'));
}
/**
* Download image or file
*

View File

@ -0,0 +1,78 @@
<?php
namespace Webkul\Shop\Http\Controllers;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Webkul\Category\Models\CategoryTranslation;
use Webkul\Product\Models\ProductFlat;
use Webkul\Category\Repositories\CategoryRepository;
use Webkul\Product\Repositories\ProductRepository;
class ProductsCategoriesProxyController extends Controller
{
/**
* Contains route related configuration
*
* @var array
*/
protected $_config;
/**
* CategoryRepository object
*
* @var CategoryRepository
*/
protected $categoryRepository;
/**
* ProductRepository object
*
* @var ProductRepository
*/
protected $productRepository;
/**
* Create a new controller instance.
*
* @param CategoryRepository $categoryRepository
* @param ProductRepository $productRepository
*
* @return void
*/
public function __construct(CategoryRepository $categoryRepository, ProductRepository $productRepository)
{
$this->categoryRepository = $categoryRepository;
$this->productRepository = $productRepository;
$this->_config = request('_config');
}
/**
* Display a listing of the resource which can be a category or a product.
*
*
* @param string $slugOrPath
*
* @return \Illuminate\View\View
*/
public function index(string $slugOrPath)
{
if ($category = $this->categoryRepository->findByPath($slugOrPath)) {
return view($this->_config['category_view'], compact('category'));
}
if ($product = $this->productRepository->findBySlug($slugOrPath)) {
$customer = auth()->guard('customer')->user();
return view($this->_config['product_view'], compact('product', 'customer'));
}
abort(404);
}
}

View File

@ -13,11 +13,6 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
//unsubscribe
Route::get('/unsubscribe/{token}', 'Webkul\Shop\Http\Controllers\SubscriptionController@unsubscribe')->name('shop.unsubscribe');
//Store front header nav-menu fetch
Route::get('/categories/{slug}', 'Webkul\Shop\Http\Controllers\CategoryController@index')->defaults('_config', [
'view' => 'shop::products.index'
])->name('shop.categories.index');
//Store front search
Route::get('/search', 'Webkul\Shop\Http\Controllers\SearchController@index')->defaults('_config', [
'view' => 'shop::search.search'
@ -89,11 +84,6 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
//Shop buynow button action
Route::get('move/wishlist/{id}', 'Webkul\Shop\Http\Controllers\CartController@moveToWishlist')->name('shop.movetowishlist');
//Show Product Details Page(For individually Viewable Product)
Route::get('/products/{slug}', 'Webkul\Shop\Http\Controllers\ProductController@index')->defaults('_config', [
'view' => 'shop::products.view'
])->name('shop.products.index');
Route::get('/downloadable/download-sample/{type}/{id}', 'Webkul\Shop\Http\Controllers\ProductController@downloadSample')->name('shop.downloadable.download_sample');
// Show Product Review Form
@ -307,5 +297,13 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
Route::get('page/{slug}', 'Webkul\CMS\Http\Controllers\Shop\PagePresenterController@presenter')->name('shop.cms.page');
Route::get('{slugOrPath}', \Webkul\Shop\Http\Controllers\ProductsCategoriesProxyController::class . '@index')
->defaults('_config', [
'product_view' => 'shop::products.view',
'category_view' => 'shop::products.index'
])
->where('slugOrPath', '^([a-z0-9-]+\/?)+$')
->name('shop.productOrCategory.index');
Route::fallback('Webkul\Shop\Http\Controllers\HomeController@notFound');
});

View File

@ -31,7 +31,7 @@ class SubscriptionEmail extends Mailable
public function build()
{
return $this->to($this->subscriptionData['email'])
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.customer.subscription.subject'))
->view('shop::emails.customer.subscription-email')->with('data', ['content' => 'You Are Subscribed', 'token' => $this->subscriptionData['token']]);
}

View File

@ -25,6 +25,8 @@ class ShopServiceProvider extends ServiceProvider
*/
public function boot(Router $router)
{
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->loadRoutesFrom(__DIR__ . '/../Http/routes.php');
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'shop');

View File

@ -296,7 +296,7 @@
else
paymentHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
shippingMethods = response.data.shippingMethods;
@ -320,7 +320,7 @@
this_this.disable_button = false;
paymentHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
paymentMethods = response.data.paymentMethods;
@ -344,7 +344,7 @@
this_this.disable_button = false;
reviewHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
this_this.getOrderSummary();

View File

@ -19,7 +19,7 @@
@if (count($reviews) > 1)
<div class="account-action">
<a href="{{ route('customer.review.deleteall') }}">{{ __('shop::app.wishlist.deleteall') }}</a>
<a href="{{ route('customer.review.deleteall') }}">{{ __('shop::app.customer.account.wishlist.deleteall') }}</a>
</div>
@endif

View File

@ -76,6 +76,10 @@
<div class="horizontal-rule mb-10 mt-10"></div>
@endforeach
<div class="bottom-toolbar">
{{ $items->links() }}
</div>
@else
<div class="empty">
{{ __('customer::app.wishlist.empty') }}

View File

@ -189,7 +189,7 @@
<p style="font-size: 16px;color: #5E5E5E;line-height: 24px;">
{!!
__('shop::app.mail.order.help', [
'support_email' => '<a style="color:#0041FF" href="mailto:' . env('ADMIN_MAIL_TO') . '">' . env('ADMIN_MAIL_TO'). '</a>'
'support_email' => '<a style="color:#0041FF" href="mailto:' . config('mail.admin.address') . '">' . config('mail.admin.address') . '</a>'
])
!!}
</p>

View File

@ -18,7 +18,7 @@
<ul class="list-group">
@foreach ($categories as $key => $category)
<li>
<a href="{{ route('shop.categories.index', $category->slug) }}">{{ $category->name }}</a>
<a href="{{ route('shop.productOrCategory.index', $category->slug) }}">{{ $category->name }}</a>
</li>
@endforeach
</ul>

View File

@ -66,7 +66,7 @@ foreach (app('Webkul\Category\Repositories\CategoryRepository')->getVisibleCateg
<script type="text/x-template" id="category-item-template">
<li>
<a :href="url+'/categories/'+this.item['translations'][0].slug">
<a :href="url+'/'+this.item['translations'][0].url_path">
@{{ name }}&emsp;
<i class="icon dropdown-right-icon" v-if="haveChildren && item.parent_id != null"></i>
</a>

View File

@ -13,7 +13,7 @@
@endif
<div class="product-image">
<a href="{{ route('shop.products.index', $product->url_key) }}" title="{{ $product->name }}">
<a href="{{ route('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<img src="{{ $productBaseImage['medium_image_url'] }}" onerror="this.src='{{ asset('vendor/webkul/ui/assets/images/product/meduim-product-placeholder.png') }}'"/>
</a>
</div>
@ -21,7 +21,7 @@
<div class="product-information">
<div class="product-name">
<a href="{{ url()->to('/').'/products/' . $product->url_key }}" title="{{ $product->name }}">
<a href="{{ route('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<span>
{{ $product->name }}
</span>

View File

@ -15,13 +15,13 @@
<?php $productBaseImage = $productImageHelper->getProductBaseImage($product); ?>
<div class="product-image">
<a href="{{ route('shop.products.index', $product->url_key) }}" title="{{ $product->name }}">
<a href="{{ route('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<img src="{{ $productBaseImage['medium_image_url'] }}" />
</a>
</div>
<div class="product-name mt-20">
<a href="{{ url()->to('/').'/products/'.$product->url_key }}" title="{{ $product->name }}">
<a href="{{ route('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<span>{{ $product->name }}</span>
</a>
</div>

View File

@ -17,13 +17,13 @@
<div class="product-info">
<div class="product-image">
<a href="{{ route('shop.products.index', $product->url_key) }}" title="{{ $product->name }}">
<a href="{{ route('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<img src="{{ $productBaseImage['medium_image_url'] }}" />
</a>
</div>
<div class="product-name mt-20">
<a href="{{ url()->to('/').'/products/'.$product->url_key }}" title="{{ $product->name }}">
<a href="{{ ('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<span>{{ $product->name }}</span>
</a>
</div>
@ -32,7 +32,7 @@
@if ($product->getTypeInstance()->haveSpecialPrice())
<span class="pro-price">{{ core()->currency($product->getTypeInstance()->getSpecialPrice()) }}</span>
@else
<span class="pro-price">{{ core()->currency($product->price) }}</span>
<span class="pro-price">{{ core()->currency($product->getTypeInstance()->getMinimalPrice()) }}</span>
@endif
</div>
</div>

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,4 +1,4 @@
{
"/js/ui.js": "/js/ui.js?id=e4fa0e081b6cdb292efa",
"/css/ui.css": "/css/ui.css?id=b86e1a5d250154e8ff45"
"/js/ui.js": "/js/ui.js?id=ed9978ac7d054203f0bc",
"/css/ui.css": "/css/ui.css?id=0895b560bbc19259cee8"
}

View File

@ -91,7 +91,7 @@ abstract class DataGrid
/**
* If paginated then value of pagination.
*/
protected $itemsPerPage = 15;
protected $itemsPerPage = 10;
protected $operators = [
'eq' => "=",
@ -142,8 +142,6 @@ abstract class DataGrid
public function __construct()
{
$this->invoker = $this;
$this->itemsPerPage = core()->getConfigData('general.general.locale_options.admin_page_limit') ?: $this->itemsPerPage;
}
/**
@ -154,6 +152,12 @@ abstract class DataGrid
$parsedUrl = [];
$unparsed = url()->full();
$route = request()->route() ? request()->route()->getName() : "";
if ($route == 'admin.datagrid.export') {
$unparsed = url()->previous();
}
if (count(explode('?', $unparsed)) > 1) {
$to_be_parsed = explode('?', $unparsed)[1];
@ -161,6 +165,10 @@ abstract class DataGrid
unset($parsedUrl['page']);
}
$this->itemsPerPage = isset($parsedUrl['perPage']) ? $parsedUrl['perPage']['eq'] : $this->itemsPerPage;
unset($parsedUrl['perPage']);
return $parsedUrl;
}

View File

@ -16,6 +16,17 @@
position: absolute;
right: 25px;
}
.per-page {
right: 250px;
.per-page-label {
position: absolute;
right: 120px;
width: 100%;
top: 8px;
}
}
}
.filter-row-two {

View File

@ -13,6 +13,7 @@ return [
'no-records' => 'لا توجد سجلات',
'filter-fields-missing' => 'بعض الحقل المطلوب هو لاغ ، رجاء تفقد عمود ، حالة و قيمة صحيح',
'click_on_action' => 'هل تريد حقا أن تؤدي هذا العمل؟'
'click_on_action' => 'هل تريد حقا أن تؤدي هذا العمل؟',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -35,6 +35,7 @@ return [
'true' => 'True / Active',
'false' => 'False / Inactive',
'between' => 'Is between',
'apply' => 'Apply'
'apply' => 'Apply',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -31,6 +31,7 @@ return [
'true' => 'صحیح / فعال',
'false' => 'غلط / غیرفعال',
'between' => 'ما بین',
'apply' => 'درخواست'
'apply' => 'درخواست',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -31,6 +31,7 @@ return [
'true' => 'Verdadeiro / Ativo',
'false' => 'Falso / Inativo',
'between' => 'Está entre',
'apply' => 'Aplicar'
'apply' => 'Aplicar',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -130,12 +130,29 @@
</ul>
</div>
</div>
<div class="dropdown-filters per-page">
<div class="control-group">
<label class="per-page-label" for="perPage">
{{ __('ui::app.datagrid.items-per-page') }}
</label>
<select id="perPage" name="perPage" class="control" v-model="perPage" v-on:change="paginate">
<option value="10"> 10 </option>
<option value="20"> 20 </option>
<option value="30"> 30 </option>
<option value="40"> 40 </option>
<option value="50"> 50 </option>
</select>
</div>
</div>
</div>
<div class="filter-row-two">
<span class="filter-tag" v-if="filters.length > 0" v-for="filter in filters" style="text-transform: capitalize;">
<span v-if="filter.column == 'sort'">@{{ filter.label }}</span>
<span v-else-if="filter.column == 'search'">Search</span>
<span v-else-if="filter.column == 'perPage'">perPage</span>
<span v-else>@{{ filter.label }}</span>
<span class="wrapper">
@ -198,12 +215,21 @@
stringConditionSelect: false,
booleanConditionSelect: false,
numberConditionSelect: false,
datetimeConditionSelect: false
datetimeConditionSelect: false,
perPage: 10,
}
},
mounted: function() {
this.setParamsAndUrl();
if (this.filters.length) {
for (let i = 0; i < this.filters.length; i++) {
if (this.filters[i].column == 'perPage') {
this.perPage = this.filters[i].val;
}
}
}
},
methods: {
@ -513,6 +539,14 @@
newParams = '';
for(i = 0; i < this.filters.length; i++) {
if (this.filters[i].column == 'status') {
if (this.filters[i].val.includes("True")) {
this.filters[i].val = 1;
} else if (this.filters[i].val.includes("False")) {
this.filters[i].val = 0;
}
}
if (i == 0) {
newParams = '?' + this.filters[i].column + '[' + this.filters[i].cond + ']' + '=' + this.filters[i].val;
} else {
@ -601,10 +635,12 @@
select: function() {
this.allSelected = false;
if(this.dataIds.length == 0)
if (this.dataIds.length == 0) {
this.massActionsToggle = false;
else
this.massActionType = null;
} else {
this.massActionsToggle = true;
}
},
//triggered when master checkbox is clicked
@ -672,6 +708,20 @@
this.massActionsToggle = false;
this.allSelected = false;
this.massActionType = null;
},
paginate: function(e) {
for (let i = 0; i < this.filters.length; i++) {
if (this.filters[i].column == 'perPage') {
this.filters.splice(i, 1);
}
}
this.filters.push({"column":"perPage","cond":"eq","val": e.target.value});
this.makeURL();
}
}
});

0
public/index.php Executable file → Normal file
View File

View File

@ -46,7 +46,7 @@
// reading env content
$data = file($envFile);
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT'];
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT', 'DB_PREFIX'];
$key = $value = [];
if ($data) {
@ -89,7 +89,8 @@
if (!$conn->connect_error) {
// retrieving admin entry
$sql = "SELECT id, name FROM admins";
$prefix = $databaseData['DB_PREFIX'].'admins';
$sql = "SELECT id, name FROM $prefix";
$result = $conn->query($sql);
if ($result) {

View File

@ -12,7 +12,7 @@
// reading env content
$data = file($envFile);
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT'];
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT', 'DB_PREFIX'];
$key = $value = [];
if ($data) {
@ -55,7 +55,8 @@
if (! $conn->connect_error) {
// retrieving admin entry
$sql = "SELECT id, name FROM admins";
$prefix = $databaseData['DB_PREFIX'].'admins';
$sql = "SELECT id, name FROM $prefix";
$result = $conn->query($sql);
if ($result) {

View File

@ -1,32 +0,0 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* @return void
*/
public function testBasicExample()
{
$customer = app('Webkul\Customer\Repositories\CustomerRepository');
$customer = $customer->all();
$customer = $customer->first();
$this->browse(function (Browser $browser) use($customer) {
$browser->visit('/customer/login')
->type('email', $customer->email)
->type('password', $customer->password)
->click('input[type="submit"]')
->screenshot('error');
});
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class HomePage extends Page
{
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/';
}
/**
* Assert that the browser is on the page.
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
//
}
/**
* Get the element shortcuts for the page.
*
* @return array
*/
public function elements()
{
return [
'@element' => '#selector',
];
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Page as BasePage;
abstract class Page extends BasePage
{
/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ProductCategoryTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* @return void
*/
public function testGuestAddToCart()
{
$categories = app('Webkul\Category\Repositories\CategoryRepository')->all();
$products = app('Webkul\Product\Repositories\ProductRepository')->all();
$slugs = array();
foreach ($categories as $category) {
if ($category->slug != 'root') {
array_push($slugs, $category->slug);
}
}
$slugIndex = array_rand($slugs);
$testSlug = $slugs[$slugIndex];
$testProduct = array();
foreach ($products as $product) {
$categories = $product->categories;
if ($categories->last()->slug == $testSlug) {
array_push($testProduct, ['name' => $product->name, 'url_key' => $product->url_key]);
break;
}
}
$this->browse(function (Browser $browser) use($testSlug, $testProduct) {
$browser->visit(route('shop.categories.index', $testSlug));
$browser->assertSeeLink($testProduct[0]['name']);
$browser->pause(5000);
});
}
}

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -1,22 +0,0 @@
<?php
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
trait CreatesApplication
{
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
return $app;
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--no-sandbox'
]);
return RemoteWebDriver::create(
'http://localhost:9515/', DesiredCapabilities::chrome()
);
// return RemoteWebDriver::create(
// 'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
// ChromeOptions::CAPABILITY, $options
// )
// );
}
}

View File

@ -1,124 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Auth;
use Crypt;
use App;
use Faker\Generator as Faker;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Webkul\Customer\Repositories\CustomerRepository as Customer;
class AuthTest extends TestCase
{
protected $customer;
/**
* To check if the customer can view the login page or not
*
* @return void
*/
public function testCustomerLoginPage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/customer/login');
$response->assertSuccessful();
$response->assertViewIs('shop::customers.session.index');
}
public function testCustomerResgistrationPage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/customer/register');
$response->assertSuccessful();
$response->assertViewIs('shop::customers.signup.index');
}
public function testCustomerRegistration() {
$faker = \Faker\Factory::create();
$allCustomers = array();
$customers = app(Customer::class);
$created = $customers->create([
'first_name' => explode(' ', $faker->name)[0],
'last_name' => explode(' ', $faker->name)[0],
'channel_id' => core()->getCurrentChannel()->id,
'gender' => $faker->randomElement($array = array ('Male','Female', 'Other')),
'date_of_birth' => $faker->date($format = 'Y-m-d', $max = 'now'),
'email' => $faker->email,
'password' => bcrypt('12345678'),
'is_verified' => 1
]);
$this->assertEquals($created->id, $created->id);
}
public function testCustomerLogin()
{
config(['app.url' => 'http://prashant.com']);
$customers = app(Customer::class);
$customer = $customers->findOneByField('email', 'john@doe.net');
$response = $this->post('/customer/login', [
'email' => $customer->email,
'password' => '12345678'
]);
$response->assertRedirect('/customer/account/profile');
}
/**
* Test that customer cannot login with the wrong credentials.
*/
public function willNotLoginWithWrongCredentials()
{
$customers = app(Customer::class);
$customer = $customers->findOneByField('email', 'john@doe.net');
$response = $this->from(route('login'))->post(route('customer.session.create'),
[
'email' => $customer->email,
'password' => 'wrongpassword3428903mlndvsnljkvsd',
]);
$this->assertGuest();
}
/**
* Test to confirm that customer cannot login if user does not exist.
*/
public function willNotLoginWithNonexistingCustomer()
{
$response = $this->post(route('customer.session.create'), [
'email' => 'fiaiia9q2943jklq34h203qtb3o2@something.com',
'password' => 'wrong-password',
]);
$this->assertGuest();
}
/**
* To test that customer can logout
*/
public function allowsCustomerToLogout()
{
$customer = auth()->guard('customer')->user();
$this->get(route('customer.session.destroy'));
$this->assertGuest();
}
}

View File

@ -1,88 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Http\Request;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Webkul\Category\Repositories\CategoryRepository;
class GeneralTest extends TestCase
{
/**
* Test for home page
*
* @return void
*/
public function testHomePage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/');
$response->assertStatus(200);
}
/**
* Test for customer login
*
* @return void
*/
public function testCustomerLoginPage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/customer/login');
$response->assertStatus(200);
}
/**
* Test for categories page
*
* @return void
*/
public function testCategoriesPage()
{
$categoryUrlSlug = 'marvel-figurines';
config(['app.url' => 'http://prashant.com']);
$response = $this->get("/categories/{$categoryUrlSlug}");
$response->assertStatus(200);
}
/**
* Test for customer registration page
*
* @return void
*/
public function testCustomerRegistrationPage()
{
// config(['app.url' => 'http://127.0.0.1:8000']);
$response = $this->get("/customer/register");
$response->assertStatus(200);
}
/**
* Test for checkout's cart page
*
* @return void
*/
public function testCartPage()
{
// config(['app.url' => 'http://127.0.0.1:8000']);
$response = $this->get("/checkout/cart");
$response->assertStatus(200);
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
}

View File

@ -1,19 +0,0 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}

0
tests/_data/.gitkeep Normal file
View File

2
tests/_output/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,26 @@
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* Define custom actions here
*/
}

View File

@ -0,0 +1,61 @@
<?php
use Illuminate\Routing\RouteCollection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Webkul\User\Models\Admin;
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class FunctionalTester extends \Codeception\Actor
{
use _generated\FunctionalTesterActions;
/**
* Define custom actions here
*/
/**
* Login as default administrator
*/
public function loginAsAdmin(): void
{
$I = $this;
Auth::guard('admin')->login($I->grabRecord(Admin::class, ['email' => 'admin@example.com']));
$I->seeAuthentication('admin');
}
/**
* Go to a specific route and check if admin guard is applied on it
*
* @param string $name name of the route
* @param array|null $params params the route will be created with
*/
public function amOnAdminRoute(string $name, array $params = null): void
{
$I = $this;
$I->amOnRoute($name, $params);
$I->seeCurrentRouteIs($name);
/** @var RouteCollection $routes */
$routes = Route::getRoutes();
$middlewares = $routes->getByName($name)->middleware();
$I->assertContains('admin', $middlewares, 'check that admin middleware is applied');
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Acceptance extends \Codeception\Module
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Functional extends \Codeception\Module
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Trigger extends \Codeception\Module
{
}

Some files were not shown because too many files have changed in this diff Show More