from server
This commit is contained in:
parent
f196747335
commit
570ecad551
|
|
@ -11,6 +11,11 @@ use Illuminate\Support\Facades\Validator;
|
|||
use RainLab\Blog\Models\Post;
|
||||
use TPS\Birzha\Classes\BlogPostResource;
|
||||
use TPS\Birzha\Models\SliderApp;
|
||||
use TPS\Birzha\Models\UserSliders;
|
||||
use TPS\Birzha\Models\Product;
|
||||
use TPS\Birzha\Models\City;
|
||||
use TPS\Birzha\Models\Favourites;
|
||||
use TPS\Birzha\Models\Comment;
|
||||
use RainLab\User\Models\User;
|
||||
|
||||
class BlogPostsApiController extends Controller
|
||||
|
|
@ -19,10 +24,12 @@ class BlogPostsApiController extends Controller
|
|||
|
||||
protected $helpers;
|
||||
|
||||
public function __construct(Post $Post, Helpers $helpers)
|
||||
public function __construct(Post $Post, Favourites $Favourites, Comment $Comment, Helpers $helpers)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->Post = $Post;
|
||||
$this->Favourites = $Favourites;
|
||||
$this->Comment = $Comment;
|
||||
$this->helpers = $helpers;
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +52,184 @@ class BlogPostsApiController extends Controller
|
|||
->paginate($data['per_page'] ?? 7))->response()->getData(), 200);
|
||||
}
|
||||
|
||||
public function getAccountSliders($id)
|
||||
{
|
||||
$data = UserSliders::where('user_id', $id)->paginate(9);
|
||||
|
||||
if($data) {
|
||||
$data->each(function ($item, $key) {
|
||||
$item->img = 'http://78.111.88.8:9086' . \Config::get('cms.storage.media.path') . $item->img;
|
||||
});
|
||||
}
|
||||
|
||||
return response()->json($data, 200);
|
||||
}
|
||||
|
||||
|
||||
public function createComment(Request $request)
|
||||
{
|
||||
$currentUser = \JWTAuth::parseToken()->authenticate();
|
||||
|
||||
$data = $request->all();
|
||||
$validator = Validator::make($data, [
|
||||
'product_id' => 'required',
|
||||
'comment' => 'required',
|
||||
'rating' => 'required|numeric'
|
||||
]);
|
||||
|
||||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
|
||||
|
||||
$comment = new $this->Comment;
|
||||
//dd($favourite);
|
||||
$comment->user_id = $currentUser->id;
|
||||
$comment->product_id = (int)$data['product_id'];
|
||||
$comment->comment = $data['comment'];
|
||||
$comment->rating = $data['rating'];
|
||||
|
||||
$comment->save();
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(201, 'ok', ['comment' => $comment]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function getComment(Request $request)
|
||||
{
|
||||
//$data = $request->all();
|
||||
// $validator = Validator::make($data, [
|
||||
// 'product_id' => 'required',
|
||||
// ]);
|
||||
|
||||
// if($validator->fails()) {
|
||||
// return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
// }
|
||||
|
||||
$currentUser = \JWTAuth::parseToken()->authenticate();
|
||||
$comment = $this->Comment::where('user_id', $currentUser->id)->paginate(9);
|
||||
//dd($favourite);
|
||||
|
||||
return response()->json($comment, 200);
|
||||
|
||||
}
|
||||
|
||||
public function getProductComment(Request $request)
|
||||
{
|
||||
$data = $request->all();
|
||||
$validator = Validator::make($data, [
|
||||
'product_id' => 'required',
|
||||
]);
|
||||
|
||||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
|
||||
$comment = $this->Comment::where('product_id', $data["product_id"])->where('is_approve', 1)->paginate(15);
|
||||
//dd($favourite);
|
||||
|
||||
return response()->json($comment, 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function createFav(Request $request)
|
||||
{
|
||||
$currentUser = \JWTAuth::parseToken()->authenticate();
|
||||
|
||||
$data = $request->all();
|
||||
$validator = Validator::make($data, [
|
||||
'product_id' => 'required'
|
||||
]);
|
||||
|
||||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
|
||||
|
||||
$favourite = new $this->Favourites;
|
||||
//dd($favourite);
|
||||
$favourite->user_id = $currentUser->id;
|
||||
$favourite->product_id = (int)$data['product_id'];
|
||||
|
||||
$favourite->save();
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(201, 'ok', ['favourite' => $favourite]);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function getFav(Request $request)
|
||||
{
|
||||
$currentUser = \JWTAuth::parseToken()->authenticate();
|
||||
|
||||
|
||||
$favourite = $this->Favourites::where('user_id', $currentUser->id)
|
||||
->with(['product' => function($q){
|
||||
$q->approved()->with(['images', 'place', 'files', 'translations:locale,model_id,attribute_data']);
|
||||
}])->paginate(9);
|
||||
//dd($favourite);
|
||||
|
||||
return response()->json($favourite, 200);
|
||||
|
||||
}
|
||||
|
||||
public function deleteFav($id){
|
||||
|
||||
$favourite = $this->Favourites->find($id);
|
||||
$favourite->delete();
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(200, 'success', ['message' => 'Data has been deleted successfully']);
|
||||
}
|
||||
|
||||
|
||||
public function getAccountProducts($id)
|
||||
{
|
||||
$data = Product::where('vendor_id', $id)
|
||||
->with('categories:id,name')
|
||||
->with([
|
||||
'translations:locale,model_id,attribute_data',
|
||||
'images:attachment_id,attachment_type,disk_name,file_name',
|
||||
])
|
||||
->approved()
|
||||
->paginate(9);
|
||||
|
||||
return response()->json($data, 200);
|
||||
}
|
||||
|
||||
public function getPlaces()
|
||||
{
|
||||
$data = City::with([
|
||||
'translations:locale,model_id,attribute_data',
|
||||
])
|
||||
->paginate(100);
|
||||
|
||||
return response()->json($data, 200);
|
||||
}
|
||||
|
||||
|
||||
public function getAccountDatas($id)
|
||||
{
|
||||
//$data = User::find($id)->first;
|
||||
|
||||
|
||||
$data = User::where('id', $id)->select('id', 'name', 'email', 'username', 'type', 'logo', 'shop_title', 'slogan', 'work_time', 'description', 'map', 'banner', 'is_instagram')
|
||||
->with(['categories' => function($q){
|
||||
$q->with('translations:locale,model_id,attribute_data');
|
||||
}])
|
||||
->with('sliders')
|
||||
->first();
|
||||
|
||||
$data->pathUrl = 'http://78.111.88.8:9086' . \Config::get('cms.storage.media.path');
|
||||
|
||||
return response()->json($data, 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function getAccounts(Request $request)
|
||||
{
|
||||
|
|
@ -52,7 +237,7 @@ class BlogPostsApiController extends Controller
|
|||
|
||||
$dataAccounts = User::select('id', 'name', 'email', 'username', 'type', 'logo', 'shop_title', 'slogan', 'work_time', 'description', 'map', 'banner', 'is_instagram')
|
||||
->with('categories:id,name,slug,icon')
|
||||
->get();
|
||||
->paginate(9);
|
||||
|
||||
if($data && $data["type"] == "home"){
|
||||
|
||||
|
|
@ -94,7 +279,7 @@ class BlogPostsApiController extends Controller
|
|||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
$dataSlider = SliderApp::where('type', $data['type'])->orderBy('order', 'desc')->select('id', 'img', 'type', 'order')->get();
|
||||
$dataSlider = SliderApp::where('type', $data['type'])->orderBy('order', 'desc')->select('id', 'img', 'type', 'order')->paginate(9);
|
||||
|
||||
//$dataSlider["img"] = $path.$dataSlider["img"];
|
||||
|
||||
|
|
|
|||
|
|
@ -33,31 +33,30 @@ class CategoriesAPIController extends Controller
|
|||
|
||||
if($data["featured"] == 3){
|
||||
$dataCat = $this->Category
|
||||
->select('id', 'name', 'icon', 'status', 'slug', 'sort_order', 'is_featured')
|
||||
->select('id', 'name', 'icon', 'status', 'slug', 'sort_order', 'is_featured', 'is_file_category')
|
||||
->with('translations:locale,model_id,attribute_data')
|
||||
->orderBy('sort_order', 'asc')
|
||||
->active()
|
||||
->get();
|
||||
->paginate(9);
|
||||
}else{
|
||||
$dataCat = $this->Category
|
||||
->select('id', 'name', 'icon', 'status', 'slug', 'sort_order', 'is_featured')
|
||||
->select('id', 'name', 'icon', 'status', 'slug', 'sort_order', 'is_featured', 'is_file_category')
|
||||
->with('translations:locale,model_id,attribute_data')
|
||||
->where('is_featured', $data["featured"])
|
||||
->orderBy('sort_order', 'asc')
|
||||
->active()
|
||||
->get();
|
||||
->paginate(9);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($dataCat) {
|
||||
$dataCat->each(function ($item, $key) {
|
||||
$item->icon = $this->pageUrl('index') . \Config::get('cms.storage.media.path') . $item->icon;
|
||||
$item->icon = 'http://78.111.88.8:9086' . \Config::get('cms.storage.media.path') . $item->icon;
|
||||
});
|
||||
}
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(200, 'success', $dataCat);
|
||||
|
||||
return response()->json($dataCat, 200);
|
||||
}
|
||||
|
||||
public function show($id){
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use AhmadFatoni\ApiGenerator\Helpers\Helpers;
|
|||
use Illuminate\Support\Facades\Validator;
|
||||
use TPS\Birzha\Models\Product;
|
||||
use TPS\Birzha\Models\Category;
|
||||
use TPS\Birzha\Models\City;
|
||||
use TPS\Birzha\Models\Term;
|
||||
use TPS\Birzha\Models\Measure;
|
||||
use TPS\Birzha\Models\Settings;
|
||||
|
|
@ -28,6 +29,7 @@ class ProductsAPIController extends Controller
|
|||
}
|
||||
|
||||
public function index(){
|
||||
//dd("sdf");
|
||||
$sortOrderParam = strtolower(input('sort_order'));
|
||||
|
||||
// protect from sql injection
|
||||
|
|
@ -40,13 +42,16 @@ class ProductsAPIController extends Controller
|
|||
$categoryId = intval(input('category_id')); // intval protects from injection
|
||||
$perPage = intval(input('custom_per_page')); // intval protects from injection
|
||||
$productId = intval(input('product_id')); // intval protects from injection
|
||||
$is_home = intval(input('is_home'));
|
||||
$queryString = input('q');
|
||||
$locale = input('locale');
|
||||
|
||||
// $query = $this->Product::with('categories:id,name')
|
||||
$query = $this->Product::with([
|
||||
$query = Product::with([
|
||||
'translations:locale,model_id,attribute_data',
|
||||
'images:attachment_id,attachment_type,disk_name,file_name',
|
||||
'vendor:id,name,email,type,logo,banner,shop_title,slogan,is_instagram',
|
||||
'place',
|
||||
])
|
||||
->approved()
|
||||
->orderBy('ends_at', $sortOrder);
|
||||
|
|
@ -67,6 +72,19 @@ class ProductsAPIController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
if($is_home) { // fetch offers by the category of the product
|
||||
|
||||
$query = $this->Product::with([
|
||||
'translations:locale,model_id,attribute_data',
|
||||
'images:attachment_id,attachment_type,disk_name,file_name',
|
||||
'place',
|
||||
'vendor:id,name,email,type,logo,banner,shop_title,slogan,is_instagram'
|
||||
])
|
||||
->where('is_home', 1)
|
||||
->approved()
|
||||
->orderBy('ends_at', $sortOrder);
|
||||
}
|
||||
|
||||
if($productId) { // fetch offers with products in the same category
|
||||
|
||||
$product = $this->Product::find($productId);
|
||||
|
|
@ -98,6 +116,7 @@ class ProductsAPIController extends Controller
|
|||
->orderBy('ends_at', $sortOrder);
|
||||
}
|
||||
|
||||
|
||||
$data = $query ? $query->paginate($perPage) : null;
|
||||
|
||||
|
||||
|
|
@ -109,9 +128,12 @@ class ProductsAPIController extends Controller
|
|||
$data = $this->Product::with([
|
||||
'translations:locale,model_id,attribute_data',
|
||||
'images:attachment_id,attachment_type,disk_name,file_name',
|
||||
'vendor:id,name,surname,email,username'
|
||||
'vendor:id,name,surname,email,username,logo,banner',
|
||||
'files', 'place'
|
||||
])->find($id);
|
||||
|
||||
$data->main_path = 'http://78.111.88.8:9086' . \Config::get('cms.storage.media.path');
|
||||
|
||||
if ($data && $data->status == 'approved' /*&& $data->ends_at >= \Carbon\Carbon::now()*/){
|
||||
return response()->json($data, 200);
|
||||
} else {
|
||||
|
|
@ -123,24 +145,21 @@ class ProductsAPIController extends Controller
|
|||
// step1
|
||||
public function store(Request $request){
|
||||
$data = $request->all();
|
||||
|
||||
// dd($data);
|
||||
$rules = [
|
||||
'name_ru' => 'required',
|
||||
// 'name_en' => 'required',
|
||||
'name_tm' => 'required',
|
||||
|
||||
'price' => 'required|numeric',
|
||||
'place' => 'required',
|
||||
'place_id' => 'required',
|
||||
'description_tm' => 'required',
|
||||
|
||||
'description_ru' => 'required',
|
||||
|
||||
'old_img' => 'array',
|
||||
'new_img' => 'array',
|
||||
|
||||
'is_file' => 'required',m
|
||||
// allowed only jpg & png files with size <= 1024
|
||||
'new_img.*' => 'mimes:jpg,png|max:1024',
|
||||
|
||||
//'new_file.*' => 'max:9024',
|
||||
|
||||
'category_id' => [
|
||||
'required',
|
||||
'exists:tps_birzha_categories,id',
|
||||
|
|
@ -153,6 +172,9 @@ class ProductsAPIController extends Controller
|
|||
}
|
||||
],
|
||||
|
||||
|
||||
//'place_id' => 'required',
|
||||
|
||||
];
|
||||
|
||||
$validator = $this->validateForm($data, $rules);
|
||||
|
|
@ -160,43 +182,104 @@ class ProductsAPIController extends Controller
|
|||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
|
||||
// validate if no old images and no new images
|
||||
if(!$request->has('new_img')) {
|
||||
return response()->json(ErrorResponseApi::translateErrorMessage('atleast_1_image'), 400);
|
||||
}
|
||||
|
||||
// if new_img field is passed but empty
|
||||
if($request->has('new_img')) {
|
||||
$rules = [
|
||||
'new_img' => 'required'
|
||||
];
|
||||
$validator = $this->validateForm($data, $rules);
|
||||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$category = Category::find($data['category_id']);
|
||||
|
||||
if(isset($data['productForEditing'])) { // if product is being edited - not added
|
||||
|
||||
if(!$product = $this->checkUserHasProduct($data['productForEditing'])) {
|
||||
return $this->helpers->apiArrayResponseBuilder(404, 'not found', ['error' => "Resource id = {$data['productForEditing']} could not be found"]);
|
||||
}
|
||||
|
||||
//approved edilen produkty prodlenie uchin drafta tayinlamak, bagly transacsiasyny goparmak,
|
||||
if($product && $product->status == 'approved' && $product->transaction)
|
||||
{
|
||||
$product->transaction()->update(['description'=>"Lot #{$product->id} {$product->name} harydyn onki publikasia tolegidir.(bu haryt prodlenia uchin taze transaksia doredilyar)"]);
|
||||
$product->transaction = null;
|
||||
}
|
||||
} else {
|
||||
$product = new $this->Product;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$product->translateContext('tm');
|
||||
|
||||
$product->name = $data['name_tm'];
|
||||
// Sets a single translated attribute for a language
|
||||
$product->setAttributeTranslated('name', $data['name_ru'], 'ru');
|
||||
// $product->setAttributeTranslated('name', $data['name_en'], 'en');
|
||||
|
||||
$product->translateContext('tm');
|
||||
|
||||
$product->description = $data['description_tm'];
|
||||
// Sets a single translated attribute for a language
|
||||
$product->setAttributeTranslated('description', $data['description_ru'], 'ru');
|
||||
|
||||
$product->setAttributeTranslated('slug', \Str::slug($data['name_ru'],'-'), 'ru');
|
||||
// $product->setAttributeTranslated('slug', \Str::slug($data['name_en'],'-'), 'en');
|
||||
|
||||
$product->slug = \Str::slug($data['name_tm'],'-');
|
||||
$product->status = 'draft';
|
||||
$product->status = 'new';
|
||||
|
||||
$product->place_id = $data['place_id'];
|
||||
$product->price = $data['price'];
|
||||
|
||||
$product->is_file_product = $data['is_file'];
|
||||
|
||||
if($data['is_file'] == 1){
|
||||
// validate if no old images and no new images
|
||||
if(!$request->has('new_file')) {
|
||||
return response()->json(ErrorResponseApi::translateErrorMessage('atleast_1_file'), 400);
|
||||
}
|
||||
|
||||
// if new_img field is passed but empty
|
||||
if($request->has('new_file')) {
|
||||
$rules = [
|
||||
'new_file' => 'required'
|
||||
];
|
||||
$validator = $this->validateForm($data, $rules);
|
||||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
|
||||
foreach($data['new_file'] ?? [] as $key => $fileq) {
|
||||
$product->files = $fileq;
|
||||
$product->save();
|
||||
}
|
||||
|
||||
} catch(\Throwable $e) {
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(500, 'server error', ['message' => 'something went wrong file']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$product->vendor_id = \JWTAuth::parseToken()->authenticate()->id;
|
||||
|
||||
$product->ends_at = null; // if approved but date was expired
|
||||
|
||||
|
||||
try {
|
||||
|
||||
foreach($data['new_img'] ?? [] as $key => $img) {
|
||||
$product->images = $img;
|
||||
$product->save();
|
||||
}
|
||||
|
||||
} catch(\Throwable $e) {
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(500, 'server error', ['message' => 'qqqsomething went wrong']);
|
||||
}
|
||||
|
||||
// product found, validation passed -> can fill product &save
|
||||
|
||||
if(!isset($data['productForEditing'])) {
|
||||
// $product->created_at = Carbon::now('Asia/Ashgabat');
|
||||
$category->products()->save($product);
|
||||
|
|
@ -207,6 +290,10 @@ class ProductsAPIController extends Controller
|
|||
$category->products()->save($product);
|
||||
}
|
||||
|
||||
|
||||
//$produt->save();
|
||||
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(200, 'ok', ['product' => $product]);
|
||||
}
|
||||
|
||||
|
|
@ -413,16 +500,11 @@ class ProductsAPIController extends Controller
|
|||
$attachedProduct->description = $data['description_tm'];
|
||||
// Sets a single translated attribute for a language
|
||||
$attachedProduct->setAttributeTranslated('description', $data['description_ru'], 'ru');
|
||||
$attachedProduct->setAttributeTranslated('description', $data['description_en'], 'en');
|
||||
|
||||
$attachedProduct->quantity = $data['quantity'];
|
||||
|
||||
|
||||
$attachedProduct->price = $data['price'];
|
||||
$attachedProduct->measure_id = $data['measure_id'];
|
||||
$attachedProduct->payment_term_id = $data['payment_term_id'];
|
||||
$attachedProduct->delivery_term_id = $data['delivery_term_id'];
|
||||
$attachedProduct->packaging = $data['packaging'];
|
||||
$attachedProduct->place = $data['place'];
|
||||
$attachedProduct->currency_id = $data['currency_id'];
|
||||
$attachedProduct->save();
|
||||
|
||||
return $attachedProduct;
|
||||
|
|
@ -430,21 +512,18 @@ class ProductsAPIController extends Controller
|
|||
|
||||
public function myProducts()
|
||||
{
|
||||
dd("sdf");
|
||||
$perPage = intval(input('custom_per_page')); // intval protects from injection
|
||||
|
||||
try {
|
||||
$products = \JWTAuth::parseToken()->authenticate()
|
||||
->products()
|
||||
->with('translations:locale,model_id,attribute_data',
|
||||
'images:attachment_id,attachment_type,disk_name,file_name')
|
||||
->with(['translations:locale,model_id,attribute_data',
|
||||
'images:attachment_id,attachment_type,disk_name,file_name', 'place', 'files'])
|
||||
->orderBy('updated_at', 'desc')
|
||||
->paginate($perPage);
|
||||
|
||||
if($products) {
|
||||
foreach ($products as $product) {
|
||||
$product->unit = new MeasureResource($product->measure);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
return $this->helpers->apiArrayResponseBuilder(500, 'server error', ['message' => 'Something went wrong']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
<?php namespace AhmadFatoni\ApiGenerator\Controllers\API;
|
||||
|
||||
use Cms\Classes\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use AhmadFatoni\ApiGenerator\Helpers\Helpers;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
//use TPS\Birzha\Models\Term;
|
||||
use RainLab\User\Models\User as Vendor;
|
||||
|
||||
class VendorApiController extends Controller
|
||||
{
|
||||
protected $Vendor;
|
||||
|
||||
protected $helpers;
|
||||
|
||||
public function __construct(Vendor $Vendor, Helpers $helpers)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->Vendor = $Vendor;
|
||||
$this->helpers = $helpers;
|
||||
}
|
||||
|
||||
public function updateVendor(Request $request){
|
||||
|
||||
$data = $request->all();
|
||||
|
||||
$validator = Validator::make($data, [
|
||||
'shop_title' => 'required',
|
||||
'slogan' => 'required',
|
||||
'work_time' => 'required',
|
||||
'description' => 'required',
|
||||
]);
|
||||
|
||||
|
||||
|
||||
// $validator = $this->validateForm($data, $rules);
|
||||
if($validator->fails()) {
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() );
|
||||
}
|
||||
//dd($data);
|
||||
$currentUser = \JWTAuth::parseToken()->authenticate();
|
||||
$vendor = Vendor::find($currentUser->id)->first();
|
||||
|
||||
//dd($vendor->type);
|
||||
|
||||
if ($vendor->type != "simple"){
|
||||
|
||||
$vendor->shop_title = $data["shop_title"];
|
||||
$vendor->slogan = $data["slogan"];
|
||||
$vendor->work_time = $data["work_time"];
|
||||
$vendor->description = $data["description"];
|
||||
$vendor->save();
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(200, 'ok', [$vendor]);
|
||||
|
||||
}else{
|
||||
|
||||
return $this->helpers->apiArrayResponseBuilder(400, 'fail');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function getAfterFilters() {return [];}
|
||||
public static function getBeforeFilters() {return [];}
|
||||
public static function getMiddleware() {return [];}
|
||||
public function callAction($method, $parameters=false) {
|
||||
return call_user_func_array(array($this, $method), $parameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,17 +9,12 @@ Route::group(['prefix' =>'api/v1','namespace' =>'AhmadFatoni\ApiGenerator\Contro
|
|||
// Route::get('categories/{id}/delete', ['as' => 'categories.delete', 'uses' => 'CategoriesAPIController@destroy']);
|
||||
|
||||
Route::get('products', ['as' => 'products.index', 'uses' => 'ProductsApiController@index']);
|
||||
// Route::get('products', 'ProductsApiController@index');
|
||||
Route::get('products/{id}', ['as' => 'products.show', 'uses' => 'ProductsApiController@show']);
|
||||
Route::get('test',['as' => 'test', 'uses' => 'SmsController@index']);
|
||||
|
||||
// Route::get('products/{id}/delete', ['as' => 'products.delete', 'uses' => 'ProductsApiController@destroy']);
|
||||
|
||||
// Route::resource('countries', 'CountriesapiController', ['except' => ['destroy', 'create', 'edit']]);
|
||||
// Route::resource('currencies', 'CurrenciesapiController', ['except' => ['destroy', 'create', 'edit']]);
|
||||
// Route::resource('measures', 'MeasuresapiController', ['except' => ['destroy', 'create', 'edit']]);
|
||||
|
||||
|
||||
// Route::get('measures/{id}/delete', ['as' => 'measures.delete', 'uses' => 'MeasuresapiController@destroy']);
|
||||
|
||||
Route::resource('terms', 'TermsapiController', ['except' => ['destroy', 'create', 'edit']]);
|
||||
// Route::get('terms/{id}/delete', ['as' => 'terms.delete', 'uses' => 'TermsapiController@destroy']);
|
||||
|
|
@ -30,19 +25,37 @@ Route::group(['prefix' =>'api/v1','namespace' =>'AhmadFatoni\ApiGenerator\Contro
|
|||
Route::get('news/{id}', 'BlogPostsApiController@show')->where(['id' => '[0-9]+']);
|
||||
|
||||
Route::get('sliders', 'BlogPostsApiController@getSliders');
|
||||
|
||||
Route::get('places', 'BlogPostsApiController@getPlaces');
|
||||
|
||||
Route::get('accounts', 'BlogPostsApiController@getAccounts');
|
||||
|
||||
Route::get('get/product/comments', 'BlogPostsApiController@getProductComment');
|
||||
|
||||
// Route::get('account/{id}/sliders', 'BlogPostsApiController@getAccountSliders')->where(['id' => '[0-9]+']);
|
||||
Route::get('account/{id}/products', 'BlogPostsApiController@getAccountProducts')->where(['id' => '[0-9]+']);
|
||||
Route::get('account/{id}/datas', 'BlogPostsApiController@getAccountDatas');
|
||||
|
||||
Route::middleware(['\Tymon\JWTAuth\Middleware\GetUserFromToken'])->group(function () {
|
||||
Route::post('update/vendor/data', 'VendorApiController@updateVendor');
|
||||
|
||||
Route::post('create/fav', 'BlogPostsApiController@createFav');
|
||||
Route::get('get/fav', 'BlogPostsApiController@getFav');
|
||||
Route::delete('fav/{id}', 'BlogPostsApiController@deleteFav');
|
||||
|
||||
Route::post('create/comment', 'BlogPostsApiController@createComment');
|
||||
Route::get('get/customer/comments', 'BlogPostsApiController@getComment');
|
||||
|
||||
Route::post('products', 'ProductsApiController@store');
|
||||
Route::post('products/{id}', 'ProductsApiController@update')
|
||||
Route::post('products/{id}', 'ProductsApiController@update')->where('id', '[0-9]+');
|
||||
|
||||
->where('id', '[0-9]+');
|
||||
Route::delete('products/{id}/image-delete/{image_id}', 'ProductsApiController@imageDelete')
|
||||
->where(['id' => '[0-9]+', 'image_id' => '[0-9]+']);
|
||||
Route::post('products/{id}/publish', 'ProductsApiController@publish')
|
||||
->where('id', '[0-9]+');
|
||||
Route::get('my-products/','ProductsApiController@myProducts');
|
||||
|
||||
|
||||
Route::get('my-products','ProductsApiController@customerProducts');
|
||||
Route::get('my-products/{id}','ProductsApiController@showMyProductById');
|
||||
Route::delete('my-products/{id}', 'ProductsApiController@delete')
|
||||
->where('id', '[0-9]+');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# MIT license
|
||||
|
||||
Copyright (c) 2014-2022 Responsiv Pty Ltd, October CMS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php namespace RainLab\Notify;
|
||||
|
||||
use Backend;
|
||||
use System\Classes\PluginBase;
|
||||
use System\Classes\SettingsManager;
|
||||
|
||||
/**
|
||||
* Plugin registration file
|
||||
*/
|
||||
class Plugin extends PluginBase
|
||||
{
|
||||
/**
|
||||
* pluginDetails
|
||||
*/
|
||||
public function pluginDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Notify',
|
||||
'description' => 'Notification Services',
|
||||
'author' => 'Alexey Bobkov, Samuel Georges',
|
||||
'icon' => 'icon-bullhorn'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* registerSettings
|
||||
*/
|
||||
public function registerSettings()
|
||||
{
|
||||
return [
|
||||
'notifications' => [
|
||||
'label' => 'Notification Rules',
|
||||
'description' => 'Manage the events and actions that trigger notifications.',
|
||||
'category' => SettingsManager::CATEGORY_NOTIFICATIONS,
|
||||
'icon' => 'icon-bullhorn',
|
||||
'url' => Backend::url('rainlab/notify/notifications'),
|
||||
'permissions' => ['rainlab.notify.manage_notifications'],
|
||||
'order' => 600,
|
||||
'keywords' => 'notify'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* registerNotificationRules
|
||||
*/
|
||||
public function registerNotificationRules()
|
||||
{
|
||||
return [
|
||||
'groups' => [],
|
||||
'events' => [],
|
||||
'actions' => [
|
||||
\RainLab\Notify\NotifyRules\SaveDatabaseAction::class,
|
||||
\RainLab\Notify\NotifyRules\SendMailTemplateAction::class,
|
||||
],
|
||||
'conditions' => [
|
||||
\RainLab\Notify\NotifyRules\ExecutionContextCondition::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* registerPermissions
|
||||
*/
|
||||
public function registerPermissions()
|
||||
{
|
||||
return [
|
||||
'rainlab.notify.manage_notifications' => [
|
||||
'tab' => SettingsManager::CATEGORY_NOTIFICATIONS,
|
||||
'label' => 'Notifications management'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,400 @@
|
|||
# Notification engine
|
||||
|
||||
**Plugin is currently in Beta status. Proceed with caution.**
|
||||
|
||||
Adds support for sending notifications across a variety of different channels, including mail, SMS and Slack.
|
||||
|
||||
Notifications are managed in the back-end area by navigating to *Settings > Notification rules*.
|
||||
|
||||
## Notification workflow
|
||||
|
||||
When a notification fires, it uses the following workflow:
|
||||
|
||||
1. Plugin registers associated actions, conditions and events using `registerNotificationRules`
|
||||
1. A notification class is bound to a system event using `Notifier::bindEvent`
|
||||
1. A system event is fired `Event::fire`
|
||||
1. The parameters of the event are captured, along with any global context parameters
|
||||
1. A command is pushed on the queue to process the notification `Queue::push`
|
||||
1. The command finds all notification rules using the notification class and triggers them
|
||||
1. The notification conditions are checked and only proceed if met
|
||||
1. The notification actions are triggered
|
||||
|
||||
Here is an example of a plugin registering notification rules. The `groups` definition will create containers that are used to better organise events. The `presets` definition specifies notification rules defined by the system.
|
||||
|
||||
```php
|
||||
public function registerNotificationRules()
|
||||
{
|
||||
return [
|
||||
'events' => [
|
||||
\RainLab\User\NotifyRules\UserActivatedEvent::class,
|
||||
],
|
||||
'actions' => [
|
||||
\RainLab\User\NotifyRules\SaveToDatabaseAction::class,
|
||||
],
|
||||
'conditions' => [
|
||||
\RainLab\User\NotifyRules\UserAttributeCondition::class
|
||||
],
|
||||
'groups' => [
|
||||
'user' => [
|
||||
'label' => 'User',
|
||||
'icon' => 'icon-user'
|
||||
],
|
||||
],
|
||||
'presets' => '$/rainlab/user/config/notify_presets.yaml',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
Here is an example of triggering a notification. The system event `rainlab.user.activate` is bound to the `UserActivatedEvent` class.
|
||||
|
||||
```php
|
||||
// Bind to a system event
|
||||
\RainLab\Notify\Classes\Notifier::bindEvents([
|
||||
'rainlab.user.activate' => \RainLab\User\NotifyRules\UserActivatedEvent::class
|
||||
]);
|
||||
|
||||
// Fire the system event
|
||||
Event::fire('rainlab.user.activate', [$this]);
|
||||
```
|
||||
|
||||
Here is an example of registering context parameters, which are available globally to all notifications.
|
||||
|
||||
```php
|
||||
\RainLab\Notify\Classes\Notifier::instance()->registerCallback(function($manager) {
|
||||
$manager->registerGlobalParams([
|
||||
'user' => Auth::getUser()
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
Here is an example of an event preset:
|
||||
|
||||
```yaml
|
||||
# ===================================
|
||||
# Event Presets
|
||||
# ===================================
|
||||
|
||||
welcome_email:
|
||||
name: Send welcome email to user
|
||||
event: RainLab\User\NotifyRules\UserRegisteredEvent
|
||||
items:
|
||||
- action: RainLab\Notify\NotifyRules\SendMailTemplateAction
|
||||
mail_template: rainlab.user::mail.welcome
|
||||
send_to_mode: user
|
||||
conditions:
|
||||
- condition: RainLab\Notify\NotifyRules\ExecutionContextCondition
|
||||
subcondition: environment
|
||||
operator: is
|
||||
value: dev
|
||||
condition_text: Application environment <span class="operator">is</span> dev
|
||||
```
|
||||
|
||||
## Creating Event classes
|
||||
|
||||
An event class is responsible for preparing the parameters passed to the conditions and actions. The static method `makeParamsFromEvent` will take the arguments provided by the system event and convert them in to parameters.
|
||||
|
||||
```php
|
||||
class UserActivatedEvent extends \RainLab\Notify\Classes\EventBase
|
||||
{
|
||||
/**
|
||||
* @var array Local conditions supported by this event.
|
||||
*/
|
||||
public $conditions = [
|
||||
\RainLab\User\NotifyRules\UserAttributeCondition::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns information about this event, including name and description.
|
||||
*/
|
||||
public function eventDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Activated',
|
||||
'description' => 'A user is activated',
|
||||
'group' => 'user'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the usable parameters provided by this class.
|
||||
*/
|
||||
public function defineParams()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'title' => 'Name',
|
||||
'label' => 'Name of the user',
|
||||
],
|
||||
// ...
|
||||
];
|
||||
}
|
||||
|
||||
public static function makeParamsFromEvent(array $args, $eventName = null)
|
||||
{
|
||||
return [
|
||||
'user' => array_get($args, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Action classes
|
||||
|
||||
Action classes define the final step in a notification and subsequently perform the notification itself. Some examples might be sending and email or writing to the database.
|
||||
|
||||
```php
|
||||
class SendMailTemplateAction extends \RainLab\Notify\Classes\ActionBase
|
||||
{
|
||||
/**
|
||||
* Returns information about this event, including name and description.
|
||||
*/
|
||||
public function actionDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Compose a mail message',
|
||||
'description' => 'Send a message to a recipient',
|
||||
'icon' => 'icon-envelope'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Field configuration for the action.
|
||||
*/
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
$template = $this->host->template_name;
|
||||
|
||||
return 'Send a message using '.$template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers this action.
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function triggerAction($params)
|
||||
{
|
||||
$email = 'test@email.tld';
|
||||
$template = $this->host->template_name;
|
||||
|
||||
Mail::sendTo($email, $template, $params);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A form fields definition file is used to provide form fields when the action is established. These values are accessed from condition using the host model via the `$this->host` property.
|
||||
|
||||
```yaml
|
||||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
template_name:
|
||||
label: Template name
|
||||
type: text
|
||||
```
|
||||
|
||||
An action may choose to provide no form fields by simply returning false from the `defineFormFields` method.
|
||||
|
||||
```php
|
||||
public function defineFormFields()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Condition classes
|
||||
|
||||
A condition class should specify how it should appear in the user interface, providing a name, title and summary text. It also must declare an `isTrue` method for evaluating whether the condition is true or not.
|
||||
|
||||
```php
|
||||
class MyCondition extends \RainLab\Notify\Classes\ConditionBase
|
||||
{
|
||||
/**
|
||||
* Return either ConditionBase::TYPE_ANY or ConditionBase::TYPE_LOCAL
|
||||
*/
|
||||
public function getConditionType()
|
||||
{
|
||||
// If the condition should appear for all events
|
||||
return ConditionBase::TYPE_ANY;
|
||||
|
||||
// If the condition should appear only for some events
|
||||
return ConditionBase::TYPE_LOCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field configuration for the condition.
|
||||
*/
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'My condition is checked';
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'My condition';
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
$value = $this->host->mycondition;
|
||||
|
||||
return 'My condition <span class="operator">is</span> '.$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the condition is TRUE for specified parameters
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(&$params)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A form fields definition file is used to provide form fields when the condition is established. These values are accessed from condition using the host model via the `$this->host` property.
|
||||
|
||||
```yaml
|
||||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
mycondition:
|
||||
label: My condition
|
||||
type: dropdown
|
||||
options:
|
||||
true: True
|
||||
false: False
|
||||
```
|
||||
|
||||
## Model attribute condition classes
|
||||
|
||||
Model attribute conditions are designed specially for applying conditions to sets of model attributes.
|
||||
|
||||
```php
|
||||
class UserAttributeCondition extends \RainLab\Notify\Classes\ModelAttributesConditionBase
|
||||
{
|
||||
protected $modelClass = \RainLab\User\Models\User::class;
|
||||
|
||||
public function getGroupingTitle()
|
||||
{
|
||||
return 'User attribute';
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'User attribute';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the condition is TRUE for specified parameters
|
||||
* @param array $params Specifies a list of parameters as an associative array.
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(&$params)
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
|
||||
$attribute = $hostObj->subcondition;
|
||||
|
||||
if (!$user = array_get($params, 'user')) {
|
||||
throw new ApplicationException('Error evaluating the user attribute condition: the user object is not found in the condition parameters.');
|
||||
}
|
||||
|
||||
return parent::evalIsTrue($user);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An attributes definition file is used to specify which attributes should be included in the condition.
|
||||
|
||||
```yaml
|
||||
# ===================================
|
||||
# Condition Attribute Definitions
|
||||
# ===================================
|
||||
|
||||
attributes:
|
||||
|
||||
name:
|
||||
label: Name
|
||||
|
||||
email:
|
||||
label: Email address
|
||||
|
||||
country:
|
||||
label: Country
|
||||
type: relation
|
||||
relation:
|
||||
model: RainLab\Location\Models\Country
|
||||
label: Name
|
||||
nameFrom: name
|
||||
keyFrom: id
|
||||
```
|
||||
|
||||
## Save to database action
|
||||
|
||||
There is a dedicated table in the database for storing events and their parameters. This table is accessed using the `RainLab\Notify\Models\Notification` model and can be referenced as a relation from your own models. In this example the `MyProject` model contains its own notification channel called `notifications`.
|
||||
|
||||
```php
|
||||
class MyProject extends Model
|
||||
{
|
||||
// ...
|
||||
|
||||
public $morphMany = [
|
||||
'my_notifications' => [
|
||||
\RainLab\Notify\Models\Notification::class,
|
||||
'name' => 'notifiable'
|
||||
]
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
This channel should be registered with the `RainLab\Notify\NotifyRules\SaveDatabaseAction` so it appears as a related object when selecting the action.
|
||||
|
||||
```php
|
||||
SaveDatabaseAction::extend(function ($action) {
|
||||
$action->addTableDefinition([
|
||||
'label' => 'Project activity',
|
||||
'class' => MyProject::class,
|
||||
'relation' => 'my_notifications',
|
||||
'param' => 'project'
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
The **label** is shown as the related object, the **class** references the model class, the **relation** refers to the relation name. The **param** defines the parameter name, passed to the triggering event.
|
||||
|
||||
So essentially if you pass a `project` to the event parameters, or if `project` is a global parameter, a notification model is created with the parameters stored in the `data` attribute. Equivalent to the following code:
|
||||
|
||||
```php
|
||||
$myproject->my_notifications()->create([
|
||||
// ...
|
||||
'data' => $params
|
||||
]);
|
||||
```
|
||||
|
||||
## Dynamically adding conditions to events
|
||||
|
||||
Events can be extended to include new local conditions. Simply add the condition class to the event `$conditions` array property.
|
||||
|
||||
```php
|
||||
UserActivatedEvent::extend(function($event) {
|
||||
$event->conditions[] = \RainLab\UserPlus\NotifyRules\UserLocationAttributeCondition::class;
|
||||
});
|
||||
```
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use System\Classes\PluginManager;
|
||||
use October\Rain\Extension\ExtensionBase;
|
||||
use RainLab\Notify\Interfaces\Action as ActionInterface;
|
||||
|
||||
/**
|
||||
* Notification action base class
|
||||
*
|
||||
* @package rainlab\notify
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ActionBase extends ExtensionBase implements ActionInterface
|
||||
{
|
||||
use \System\Traits\ConfigMaker;
|
||||
use \System\Traits\ViewMaker;
|
||||
|
||||
/**
|
||||
* @var Model host object
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var mixed Extra field configuration for the condition.
|
||||
*/
|
||||
protected $fieldConfig;
|
||||
|
||||
/**
|
||||
* Returns information about this action, including name and description.
|
||||
*/
|
||||
public function actionDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Action',
|
||||
'description' => 'Action Description',
|
||||
'icon' => 'icon-dot-circle-o'
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct($host = null)
|
||||
{
|
||||
/*
|
||||
* Paths
|
||||
*/
|
||||
$this->viewPath = $this->configPath = $this->guessConfigPathFrom($this);
|
||||
|
||||
/*
|
||||
* Parse the config, if available
|
||||
*/
|
||||
if ($formFields = $this->defineFormFields()) {
|
||||
$this->fieldConfig = $this->makeConfig($formFields);
|
||||
}
|
||||
|
||||
if (!$this->host = $host) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->boot($host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot method called when the condition class is first loaded
|
||||
* with an existing model.
|
||||
* @return array
|
||||
*/
|
||||
public function boot($host)
|
||||
{
|
||||
// Set default data
|
||||
if (!$host->exists) {
|
||||
$this->initConfigData($host);
|
||||
}
|
||||
|
||||
// Apply validation rules
|
||||
$host->rules = array_merge($host->rules, $this->defineValidationRules());
|
||||
}
|
||||
|
||||
public function triggerAction($params)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->getActionName();
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
return $this->getActionDescription();
|
||||
}
|
||||
|
||||
public function getActionName()
|
||||
{
|
||||
return array_get($this->actionDetails(), 'name');
|
||||
}
|
||||
|
||||
public function getActionDescription()
|
||||
{
|
||||
return array_get($this->actionDetails(), 'description');
|
||||
}
|
||||
|
||||
public function getActionIcon()
|
||||
{
|
||||
return array_get($this->actionDetails(), 'icon', 'icon-dot-circle-o');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra field configuration for the condition.
|
||||
*/
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this action uses form fields.
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFieldConfig()
|
||||
{
|
||||
return !!$this->fieldConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field configuration used by this model.
|
||||
*/
|
||||
public function getFieldConfig()
|
||||
{
|
||||
return $this->fieldConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes configuration data when the condition is first created.
|
||||
* @param Model $host
|
||||
*/
|
||||
public function initConfigData($host) {}
|
||||
|
||||
/**
|
||||
* Defines validation rules for the custom fields.
|
||||
* @return array
|
||||
*/
|
||||
public function defineValidationRules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins over types registered in plugin base class with `registerNotificationRules`.
|
||||
* @return array
|
||||
*/
|
||||
public static function findActions()
|
||||
{
|
||||
$results = [];
|
||||
$bundles = PluginManager::instance()->getRegistrationMethodValues('registerNotificationRules');
|
||||
|
||||
foreach ($bundles as $plugin => $bundle) {
|
||||
foreach ((array) array_get($bundle, 'actions', []) as $conditionClass) {
|
||||
if (!class_exists($conditionClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$obj = new $conditionClass;
|
||||
$results[$conditionClass] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use RainLab\Notify\Interfaces\CompoundCondition as CompoundConditionInterface;
|
||||
|
||||
/**
|
||||
* Compound Condition Class
|
||||
*/
|
||||
class CompoundCondition extends ConditionBase implements CompoundConditionInterface
|
||||
{
|
||||
/**
|
||||
* getTitle returns a condition title for displaying in the condition settings form
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Compound Condition';
|
||||
}
|
||||
|
||||
/**
|
||||
* getText
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
$result = $this->host->condition_type == 0
|
||||
? __('ALL of subconditions should be') . ' '
|
||||
: __('ANY of subconditions should be') . ' ';
|
||||
|
||||
$result .= $this->host->condition == 'false' ? 'FALSE' : 'TRUE';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* getJoinText returns the text to use when joining two rules within.
|
||||
* @return string
|
||||
*/
|
||||
public function getJoinText()
|
||||
{
|
||||
return $this->host->condition_type == 0 ? 'AND' : 'OR';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of condition types (`ConditionBase::TYPE_*` constants)
|
||||
* that can be added to this compound condition
|
||||
*/
|
||||
public function getAllowedSubtypes()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* defineFormFields
|
||||
*/
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
public function initConfigData($host)
|
||||
{
|
||||
$host->condition_type = 0;
|
||||
$host->condition = 'true';
|
||||
}
|
||||
|
||||
public function getConditionOptions()
|
||||
{
|
||||
$options = [
|
||||
'true' => 'TRUE',
|
||||
'false' => 'FALSE'
|
||||
];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getConditionTypeOptions()
|
||||
{
|
||||
$options = [
|
||||
'0' => 'ALL subconditions should meet the requirement',
|
||||
'1' => 'ANY subconditions should meet the requirement'
|
||||
];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* getChildOptions
|
||||
*/
|
||||
public function getChildOptions(array $options)
|
||||
{
|
||||
extract(array_merge([
|
||||
'extraRules' => [],
|
||||
], $options));
|
||||
|
||||
$result = [
|
||||
'Compound condition' => CompoundCondition::class
|
||||
];
|
||||
|
||||
$classes = $extraRules + self::findConditionsByType(ConditionBase::TYPE_ANY);
|
||||
|
||||
$result = $this->addClassesSubconditions($classes, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function addClassesSubconditions($classes, $list)
|
||||
{
|
||||
foreach ($classes as $conditionClass => $obj) {
|
||||
|
||||
$subConditions = $obj->listSubconditions();
|
||||
|
||||
if ($subConditions) {
|
||||
$groupName = $obj->getGroupingTitle();
|
||||
|
||||
foreach ($subConditions as $name => $subcondition) {
|
||||
if (!$groupName) {
|
||||
$list[$name] = $conditionClass.':'.$subcondition;
|
||||
}
|
||||
else {
|
||||
if (!array_key_exists($groupName, $list)) {
|
||||
$list[$groupName] = [];
|
||||
}
|
||||
|
||||
$list[$groupName][$name] = $conditionClass.':'.$subcondition;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$list[$obj->getName()] = $conditionClass;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the condition is TRUE for specified parameters.
|
||||
*
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(&$params)
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
|
||||
$requiredConditionValue = $hostObj->condition == 'true' ? true : false;
|
||||
|
||||
foreach ($hostObj->children as $subcondition) {
|
||||
$subconditionResult = $subcondition->getConditionObject()->isTrue($params) ? true : false;
|
||||
|
||||
/*
|
||||
* All
|
||||
*/
|
||||
if ($hostObj->condition_type == 0) {
|
||||
if ($subconditionResult !== $requiredConditionValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Any
|
||||
*/
|
||||
else {
|
||||
if ($subconditionResult === $requiredConditionValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All
|
||||
*/
|
||||
if ($hostObj->condition_type == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use System\Classes\PluginManager;
|
||||
use October\Rain\Extension\ExtensionBase;
|
||||
use RainLab\Notify\Interfaces\Condition as ConditionInterface;
|
||||
|
||||
/**
|
||||
* Condition Base Class
|
||||
*/
|
||||
class ConditionBase extends ExtensionBase implements ConditionInterface
|
||||
{
|
||||
use \System\Traits\ConfigMaker;
|
||||
use \System\Traits\ViewMaker;
|
||||
|
||||
const TYPE_ANY = 'any';
|
||||
const TYPE_LOCAL = 'local';
|
||||
|
||||
const CAST_FLOAT = 'float';
|
||||
const CAST_STRING = 'string';
|
||||
const CAST_INTEGER = 'integer';
|
||||
const CAST_BOOLEAN = 'boolean';
|
||||
const CAST_RELATION = 'relation';
|
||||
|
||||
/**
|
||||
* @var Model host object
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var mixed Extra field configuration for the condition.
|
||||
*/
|
||||
protected $fieldConfig;
|
||||
|
||||
/**
|
||||
* @var string The plugin class method used to look for conditions.
|
||||
*/
|
||||
protected static $registrationMethod = 'registerNotificationRules';
|
||||
|
||||
public function __construct($host = null)
|
||||
{
|
||||
/*
|
||||
* Paths
|
||||
*/
|
||||
$this->viewPath = $this->configPath = $this->guessConfigPathFrom($this);
|
||||
|
||||
/*
|
||||
* Parse the config
|
||||
*/
|
||||
$this->fieldConfig = $this->makeConfig($this->defineFormFields());
|
||||
|
||||
if (!$this->host = $host) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->boot($host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot method called when the condition class is first loaded
|
||||
* with an existing model.
|
||||
* @return array
|
||||
*/
|
||||
public function boot($host)
|
||||
{
|
||||
// Set default data
|
||||
if (!$host->exists) {
|
||||
$this->initConfigData($host);
|
||||
}
|
||||
|
||||
// Apply validation rules
|
||||
$host->rules = array_merge($host->rules, $this->defineValidationRules());
|
||||
|
||||
// Inject view paths to the controller through the Form widget
|
||||
$host->bindEvent('model.form.filterFields', function($form) {
|
||||
$form->getController()->addViewPath($this->getViewPaths());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra field configuration for the condition.
|
||||
*/
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes configuration data when the condition is first created.
|
||||
* @param Model $host
|
||||
*/
|
||||
public function initConfigData($host) {}
|
||||
|
||||
/**
|
||||
* Defines validation rules for the custom fields.
|
||||
* @return array
|
||||
*/
|
||||
public function defineValidationRules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
return 'Condition Text';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a condition name for displaying in the condition selection drop-down menu
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Condition';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a condition title for displaying in the condition settings form
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Condition';
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should return one of the `ConditionBase::TYPE_*` constants
|
||||
* depending on a place where the condition is valid
|
||||
*/
|
||||
public function getConditionType()
|
||||
{
|
||||
return ConditionBase::TYPE_ANY;
|
||||
}
|
||||
|
||||
public function listSubconditions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a title to use for grouping subconditions
|
||||
* in the Create Condition drop-down menu
|
||||
*/
|
||||
public function getGroupingTitle()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field configuration used by this model.
|
||||
*/
|
||||
public function getFieldConfig()
|
||||
{
|
||||
return $this->fieldConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins over types registered in plugin base class with `registerNotificationRules`,
|
||||
* checks if the condition type matches and adds it to an array that is returned.
|
||||
*
|
||||
* @param string $type Use `self::TYPE_*` constants
|
||||
* @return array
|
||||
*/
|
||||
public static function findConditionsByType($type)
|
||||
{
|
||||
$results = [];
|
||||
$bundles = PluginManager::instance()->getRegistrationMethodValues(static::$registrationMethod);
|
||||
|
||||
foreach ($bundles as $plugin => $bundle) {
|
||||
foreach ((array) array_get($bundle, 'conditions', []) as $conditionClass) {
|
||||
if (!class_exists($conditionClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$obj = new $conditionClass;
|
||||
if ($obj->getConditionType() != $type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$conditionClass] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function setFormFields($fields)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function setCustomData()
|
||||
{
|
||||
}
|
||||
|
||||
public function onPreRender($controller, $widget)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the condition is TRUE for specified parameters
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(&$params)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getChildOptions(array $options)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use Str;
|
||||
use Yaml;
|
||||
use File;
|
||||
use Lang;
|
||||
use System\Classes\PluginManager;
|
||||
use October\Rain\Extension\ExtensionBase;
|
||||
use RainLab\Notify\Interfaces\Event as EventInterface;
|
||||
|
||||
/**
|
||||
* Notification event base class
|
||||
*
|
||||
* @package rainlab\notify
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class EventBase extends ExtensionBase implements EventInterface
|
||||
{
|
||||
/**
|
||||
* @var Model host object
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var array Contains the event parameter values.
|
||||
*/
|
||||
protected $params = [];
|
||||
|
||||
/**
|
||||
* @var array Local conditions supported by this event.
|
||||
*/
|
||||
public $conditions = [];
|
||||
|
||||
/**
|
||||
* Returns information about this event, including name and description.
|
||||
*/
|
||||
public function eventDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Event',
|
||||
'description' => 'Event description',
|
||||
'group' => 'groupcode'
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct($host = null)
|
||||
{
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the parameters used by this class.
|
||||
* This method should be used as an override in the extended class.
|
||||
*/
|
||||
public function defineParams()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Local conditions supported by this event.
|
||||
*/
|
||||
public function defineConditions()
|
||||
{
|
||||
return $this->conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple params.
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function setParams($params)
|
||||
{
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all params.
|
||||
* @return array
|
||||
*/
|
||||
public function getParams()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates event parameters based on arguments from the triggering system event.
|
||||
* @param array $args
|
||||
* @param string $eventName
|
||||
* @return void
|
||||
*/
|
||||
public static function makeParamsFromEvent(array $args, $eventName = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function getEventName()
|
||||
{
|
||||
return Lang::get(array_get($this->eventDetails(), 'name'));
|
||||
}
|
||||
|
||||
public function getEventDescription()
|
||||
{
|
||||
return Lang::get(array_get($this->eventDetails(), 'description'));
|
||||
}
|
||||
|
||||
public function getEventGroup()
|
||||
{
|
||||
return array_get($this->eventDetails(), 'group');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an event or action identifier from a class name or object.
|
||||
* @param mixed Class name or object
|
||||
* @return string Identifier in format of vendor-plugin-class
|
||||
*/
|
||||
public function getEventIdentifier()
|
||||
{
|
||||
$namespace = Str::normalizeClassName(get_called_class());
|
||||
if (strpos($namespace, '\\') === null) {
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
$parts = explode('\\', $namespace);
|
||||
$class = array_pop($parts);
|
||||
$slice = array_slice($parts, 1, 2);
|
||||
$code = strtolower(implode('-', $slice) . '-' . $class);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins over types registered in plugin base class with `registerNotificationRules`.
|
||||
* @return array
|
||||
*/
|
||||
public static function findEvents()
|
||||
{
|
||||
$results = [];
|
||||
$bundles = PluginManager::instance()->getRegistrationMethodValues('registerNotificationRules');
|
||||
|
||||
foreach ($bundles as $plugin => $bundle) {
|
||||
foreach ((array) array_get($bundle, 'events', []) as $conditionClass) {
|
||||
if (!class_exists($conditionClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$obj = new $conditionClass;
|
||||
$results[$conditionClass] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public static function findEventGroups()
|
||||
{
|
||||
$results = [];
|
||||
$bundles = PluginManager::instance()->getRegistrationMethodValues('registerNotificationRules');
|
||||
|
||||
foreach ($bundles as $plugin => $bundle) {
|
||||
if ($groups = array_get($bundle, 'groups')) {
|
||||
$results += $groups;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public static function findEventsByGroup($group)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach (self::findEvents() as $conditionClass => $obj) {
|
||||
if ($obj->getEventGroup() != $group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$conditionClass] = $obj;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public static function findEventByIdentifier($identifier)
|
||||
{
|
||||
foreach (self::findEvents() as $class => $obj) {
|
||||
if ($obj->getEventIdentifier() == $identifier) {
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins over preset registered in plugin base class with `registerNotificationRules`.
|
||||
* @return array
|
||||
*/
|
||||
public static function findEventPresets()
|
||||
{
|
||||
$results = [];
|
||||
$bundles = PluginManager::instance()->getRegistrationMethodValues('registerNotificationRules');
|
||||
|
||||
foreach ($bundles as $plugin => $bundle) {
|
||||
if (!$presets = array_get($bundle, 'presets')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($presets)) {
|
||||
$presets = Yaml::parse(File::get(File::symbolizePath($presets)));
|
||||
}
|
||||
|
||||
if ($presets && is_array($presets)) {
|
||||
$results += $presets;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public static function findEventPresetsByClass($className)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach (self::findEventPresets() as $code => $definition) {
|
||||
if (!$eventClass = array_get($definition, 'event')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($eventClass != $className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$code] = $definition;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class EventParams
|
||||
{
|
||||
use \Illuminate\Queue\InteractsWithQueue;
|
||||
use \Illuminate\Queue\SerializesAndRestoresModelIdentifiers;
|
||||
|
||||
protected $eventClass;
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($eventClass, array $params)
|
||||
{
|
||||
$this->eventClass = $eventClass;
|
||||
|
||||
$this->params = $this->serializeParams($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->delete();
|
||||
|
||||
Notifier::instance()->fireEvent($this->eventClass, $this->unserializeParams());
|
||||
}
|
||||
|
||||
protected function serializeParams($params)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
$result[$param] = $this->getSerializedPropertyValue($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function unserializeParams()
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->params as $param => $value) {
|
||||
$result[$param] = $this->getRestoredPropertyValue($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,743 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use Db;
|
||||
use App;
|
||||
use SystemException;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
|
||||
class ModelAttributesConditionBase extends ConditionBase
|
||||
{
|
||||
/**
|
||||
* @var string Special value to declare nothing provided.
|
||||
*/
|
||||
const NO_EVAL_VALUE = '__no_eval_value__';
|
||||
|
||||
protected $modelClass = null;
|
||||
|
||||
/**
|
||||
* @var array operators
|
||||
*/
|
||||
protected $operators = [
|
||||
'is' => 'is',
|
||||
'is_not' => 'is not',
|
||||
'equals_or_greater' => 'equals or greater than',
|
||||
'equals_or_less' => 'equals or less than',
|
||||
'contains' => 'contains',
|
||||
'does_not_contain' => 'does not contain',
|
||||
'greater' => 'greater than',
|
||||
'less' => 'less than',
|
||||
'one_of' => 'is one of',
|
||||
'not_one_of' => 'is not one of'
|
||||
];
|
||||
|
||||
protected $modelObj = null;
|
||||
protected $referenceInfo = null;
|
||||
protected $modelAttributes = null;
|
||||
|
||||
protected static $modelObjCache = [];
|
||||
protected static $attributeControlTypeCache = [];
|
||||
|
||||
public function __construct($host = null)
|
||||
{
|
||||
parent::__construct($host);
|
||||
|
||||
/*
|
||||
* This is used as a base class, so register view path from here too
|
||||
*/
|
||||
$this->addViewPath($this->guessViewPathFrom(__CLASS__));
|
||||
}
|
||||
|
||||
public function initConfigData($host)
|
||||
{
|
||||
$host->operator = 'is';
|
||||
}
|
||||
|
||||
public function setCustomData()
|
||||
{
|
||||
$this->host->condition_control_type = $this->evalControlType();
|
||||
}
|
||||
|
||||
//
|
||||
// Definitions
|
||||
//
|
||||
|
||||
/**
|
||||
* This function should return one of the `ConditionBase::TYPE_*` constants
|
||||
* depending on a place where the condition is valid
|
||||
*/
|
||||
public function getConditionType()
|
||||
{
|
||||
return ConditionBase::TYPE_LOCAL;
|
||||
}
|
||||
|
||||
public function defineModelAttributes($type = null)
|
||||
{
|
||||
return 'attributes.yaml';
|
||||
}
|
||||
|
||||
public function defineValidationRules()
|
||||
{
|
||||
return [
|
||||
'value' => 'required'
|
||||
];
|
||||
}
|
||||
|
||||
public function defineFormFields()
|
||||
{
|
||||
return plugins_path('rainlab/notify/classes/modelattributesconditionbase/fields.yaml');
|
||||
}
|
||||
|
||||
//
|
||||
// Text helpers
|
||||
//
|
||||
|
||||
public function getText()
|
||||
{
|
||||
$host = $this->host;
|
||||
$attributes = $this->listModelAttributes();
|
||||
|
||||
if (isset($attributes[$host->subcondition])) {
|
||||
$result = $this->getConditionTextPrefix($host, $attributes);
|
||||
}
|
||||
else {
|
||||
$result = __('Unknown Attribute');
|
||||
}
|
||||
|
||||
$result .= ' <span class="operator">'.array_get($this->operators, $host->operator, $host->operator).'</span> ';
|
||||
|
||||
$controlType = $this->getValueControlType();
|
||||
|
||||
if ($controlType == 'text') {
|
||||
$result .= $host->value;
|
||||
}
|
||||
else {
|
||||
$textValue = $this->getCustomTextValue();
|
||||
if ($textValue !== false) {
|
||||
return $result.' '.$textValue;
|
||||
}
|
||||
|
||||
$referenceInfo = $this->prepareReferenceListInfo();
|
||||
$modelObj = $referenceInfo->referenceModel;
|
||||
|
||||
if (!count($referenceInfo->columns)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!strlen($host->value)) {
|
||||
return $result .= '?';
|
||||
}
|
||||
|
||||
$visibleField = $referenceInfo->primaryColumn;
|
||||
|
||||
if ($controlType == 'dropdown') {
|
||||
$obj = $modelObj->where('id', $host->value)->first();
|
||||
if ($obj) {
|
||||
$result .= e($obj->{$visibleField});
|
||||
}
|
||||
}
|
||||
else {
|
||||
$ids = explode(',', $host->value);
|
||||
foreach ($ids as &$id) {
|
||||
$id = trim(e($id));
|
||||
}
|
||||
|
||||
$records = $modelObj
|
||||
->whereIn('id', $ids)
|
||||
->orderBy($visibleField)
|
||||
->get();
|
||||
|
||||
$recordNames = [];
|
||||
foreach ($records as $record) {
|
||||
$recordNames[] = $record->{$visibleField};
|
||||
}
|
||||
|
||||
$result .= '('.implode(', ', $recordNames).')';
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getConditionTextPrefix($parametersHost, $attributes)
|
||||
{
|
||||
return $attributes[$parametersHost->subcondition];
|
||||
}
|
||||
|
||||
public function getCustomTextValue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Options
|
||||
//
|
||||
|
||||
/**
|
||||
* getSubconditionOptions
|
||||
*/
|
||||
public function getSubconditionOptions()
|
||||
{
|
||||
return $this->listModelAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* getOperatorOptions
|
||||
*/
|
||||
public function getOperatorOptions()
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
$options = [];
|
||||
$attribute = $hostObj->subcondition;
|
||||
|
||||
$currentOperatorValue = $hostObj->operator;
|
||||
|
||||
$model = $this->getModelObj();
|
||||
$definitions = $this->listModelAttributeInfo();
|
||||
|
||||
if (!isset($definitions[$attribute])) {
|
||||
$options = ['none' => 'Unknown attribute selected'];
|
||||
}
|
||||
else {
|
||||
$columnType = array_get($definitions[$attribute], 'type');
|
||||
|
||||
if ($columnType != ConditionBase::CAST_RELATION) {
|
||||
if ($columnType == ConditionBase::CAST_STRING) {
|
||||
$options = [
|
||||
'is' => 'is',
|
||||
'is_not' => 'is not',
|
||||
'contains' => 'contains',
|
||||
'does_not_contain' => 'does not contain'
|
||||
];
|
||||
}
|
||||
else {
|
||||
$options = [
|
||||
'is' => 'is',
|
||||
'is_not' => 'is not',
|
||||
'equals_or_greater' => 'equals or greater than',
|
||||
'equals_or_less' => 'equals or less than',
|
||||
'greater' => 'greater than',
|
||||
'less' => 'less than'
|
||||
];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$options = [
|
||||
'is' => 'is',
|
||||
'is_not' => 'is not',
|
||||
'one_of' => 'is one of',
|
||||
'not_one_of' => 'is not one of'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_key_exists($currentOperatorValue, $options)) {
|
||||
$keys = array_keys($options);
|
||||
if (count($keys)) {
|
||||
$hostObj->operator = $options[$keys[0]];
|
||||
}
|
||||
else {
|
||||
$hostObj->operator = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getValueDropdownOptions()
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
$attribute = $hostObj->subcondition;
|
||||
$definitions = $this->listModelAttributeInfo();
|
||||
|
||||
if (!isset($definitions[$attribute])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$columnType = array_get($definitions[$attribute], 'type');
|
||||
|
||||
if ($columnType != ConditionBase::CAST_RELATION) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$referenceInfo = $this->prepareReferenceListInfo();
|
||||
$referenceModel = $referenceInfo->referenceModel;
|
||||
$nameFrom = $referenceInfo->primaryColumn;
|
||||
$keyFrom = $referenceInfo->primaryKey;
|
||||
|
||||
// Determine if the model uses a tree trait
|
||||
$treeTraits = [
|
||||
\October\Rain\Database\Traits\NestedTree::class,
|
||||
\October\Rain\Database\Traits\SimpleTree::class
|
||||
];
|
||||
$usesTree = count(array_intersect($treeTraits, class_uses($referenceModel))) > 0;
|
||||
|
||||
$results = $referenceModel->get();
|
||||
|
||||
return $usesTree
|
||||
? $results->listsNested($nameFrom, $keyFrom)
|
||||
: $results->lists($nameFrom, $keyFrom);
|
||||
}
|
||||
|
||||
//
|
||||
// Control type
|
||||
//
|
||||
|
||||
protected function evalControlType()
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
$attribute = $hostObj->subcondition;
|
||||
$operator = $hostObj->operator;
|
||||
|
||||
$definitions = $this->listModelAttributeInfo();
|
||||
|
||||
if (!isset($definitions[$attribute])) {
|
||||
return 'text';
|
||||
}
|
||||
|
||||
$columnType = array_get($definitions[$attribute], 'type');
|
||||
|
||||
if ($columnType != ConditionBase::CAST_RELATION) {
|
||||
return 'text';
|
||||
}
|
||||
else {
|
||||
if ($operator == 'is' || $operator == 'is_not') {
|
||||
return 'dropdown';
|
||||
}
|
||||
|
||||
return 'multi_value';
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueControlType()
|
||||
{
|
||||
if (App::runningInBackend()) {
|
||||
return $this->evalControlType();
|
||||
}
|
||||
|
||||
$hostObj = $this->host;
|
||||
|
||||
if ($controlType = $hostObj->condition_control_type) {
|
||||
return $controlType;
|
||||
}
|
||||
|
||||
$controlType = $this->evalControlType();
|
||||
|
||||
$this->getModelObj()
|
||||
->where('id', $hostObj->id)
|
||||
->update(['condition_control_type' => $controlType]);
|
||||
|
||||
return $hostObj->condition_control_type = $controlType;
|
||||
}
|
||||
|
||||
//
|
||||
// Attributes
|
||||
//
|
||||
|
||||
public function listSubconditions()
|
||||
{
|
||||
$attributes = $this->listModelAttributes();
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($attributes as $name => $code) {
|
||||
$result[$code] = $name;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported attributes by a condition as an array.
|
||||
* The key is the attribute and the value is the label.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function listModelAttributes()
|
||||
{
|
||||
$attributeInfo = $this->listModelAttributeInfo();
|
||||
|
||||
foreach ($attributeInfo as $attribute => $info) {
|
||||
$attributes[$attribute] = array_get($info, 'label');
|
||||
}
|
||||
|
||||
asort($attributes);
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
protected function listModelAttributeInfo()
|
||||
{
|
||||
if ($this->modelAttributes) {
|
||||
return $this->modelAttributes;
|
||||
}
|
||||
|
||||
$config = $this->makeConfig($this->defineModelAttributes($this->getConditionType()));
|
||||
|
||||
$attributes = $config->attributes ?? [];
|
||||
|
||||
/*
|
||||
* Set defaults
|
||||
*/
|
||||
foreach ($attributes as $attribute => $info) {
|
||||
if (!isset($info['type'])) {
|
||||
$attributes[$attribute]['type'] = 'string';
|
||||
}
|
||||
}
|
||||
|
||||
return $this->modelAttributes = $attributes;
|
||||
}
|
||||
|
||||
//
|
||||
// Relation based attributes
|
||||
//
|
||||
|
||||
public function getReferencePrimaryColumn($record)
|
||||
{
|
||||
$referenceInfo = $this->prepareReferenceListInfo();
|
||||
|
||||
return $record->{$referenceInfo->primaryColumn};
|
||||
}
|
||||
|
||||
public function listSelectedReferenceRecords()
|
||||
{
|
||||
$referenceInfo = $this->prepareReferenceListInfo();
|
||||
$model = $referenceInfo->referenceModel;
|
||||
|
||||
$value = $this->host->value;
|
||||
$keys = strlen($value) ? explode(',', $value) : [];
|
||||
|
||||
if (count($keys)) {
|
||||
$model = $model->whereIn('id', $keys);
|
||||
}
|
||||
else {
|
||||
$model = $model->whereRaw('id <> id');
|
||||
}
|
||||
|
||||
$orderField = $referenceInfo->primaryColumn;
|
||||
|
||||
return $model->orderBy($orderField)->get();
|
||||
}
|
||||
|
||||
public function prepareReferenceListInfo()
|
||||
{
|
||||
if (!is_null($this->referenceInfo)) {
|
||||
return $this->referenceInfo;
|
||||
}
|
||||
|
||||
$model = $this->getModelObj();
|
||||
$attribute = $this->host->subcondition;
|
||||
$definitions = $this->listModelAttributeInfo();
|
||||
$definition = array_get($definitions, $attribute);
|
||||
|
||||
$columns = array_get($definition, 'columns');
|
||||
$primaryColumn = array_get($definition, 'relation.nameFrom', 'name');
|
||||
|
||||
if ($model->hasRelation($attribute)) {
|
||||
$relationType = $model->getRelationType($attribute);
|
||||
$relationModel = $model->makeRelation($attribute);
|
||||
$relationObject = $model->{$attribute}();
|
||||
|
||||
// Some simpler relations can specify a custom local or foreign "other" key,
|
||||
// which can be detected and implemented here automagically.
|
||||
$primaryKey = in_array($relationType, ['hasMany', 'belongsTo', 'hasOne'])
|
||||
? $relationObject->getOtherKey()
|
||||
: $relationModel->getKeyName();
|
||||
}
|
||||
elseif ($relationClass = array_get($definition, 'relation.model')) {
|
||||
$relationModel = new $relationClass;
|
||||
$primaryKey = array_get($definition, 'relation.keyFrom', 'id');
|
||||
}
|
||||
else {
|
||||
throw new SystemException(sprintf('Model %s does not contain a relation "%s"', get_class($model), $attribute));
|
||||
}
|
||||
|
||||
if (!$columns) {
|
||||
$columns = [
|
||||
$primaryColumn => [
|
||||
'label' => array_get($definition, 'relation.label', '?'),
|
||||
'searchable' => true
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$this->referenceInfo = [];
|
||||
$this->referenceInfo['referenceModel'] = $relationModel;
|
||||
$this->referenceInfo['primaryKey'] = $primaryKey;
|
||||
$this->referenceInfo['primaryColumn'] = $primaryColumn;
|
||||
$this->referenceInfo['columns'] = $columns;
|
||||
|
||||
return $this->referenceInfo = (object) $this->referenceInfo;
|
||||
}
|
||||
|
||||
public function onPreRender($controller, $widget)
|
||||
{
|
||||
$controlType = $this->getValueControlType();
|
||||
|
||||
if ($controlType != 'multi_value') {
|
||||
return;
|
||||
}
|
||||
|
||||
$selectionColumn = [
|
||||
'_select_record' => [
|
||||
'label' => '',
|
||||
'sortable' => false,
|
||||
'type' => 'partial',
|
||||
'width' => '10px',
|
||||
'path' => plugins_path('rainlab/notify/classes/modelattributesconditionbase/_column_select_record.htm')
|
||||
]
|
||||
];
|
||||
|
||||
$referenceInfo = $this->prepareReferenceListInfo();
|
||||
$filterModel = $referenceInfo->referenceModel;
|
||||
$filterColumns = $selectionColumn + $referenceInfo->columns;
|
||||
|
||||
/*
|
||||
* List widget
|
||||
*/
|
||||
$config = $this->makeConfig();
|
||||
$config->columns = $filterColumns;
|
||||
$config->model = $filterModel;
|
||||
$config->alias = $widget->alias . 'List';
|
||||
$config->showSetup = false;
|
||||
$config->showCheckboxes = false;
|
||||
$config->recordsPerPage = 6;
|
||||
$listWidget = $controller->makeWidget('Backend\Widgets\Lists', $config);
|
||||
|
||||
/*
|
||||
* Search widget
|
||||
*/
|
||||
$config = $this->makeConfig();
|
||||
$config->alias = $widget->alias . 'Search';
|
||||
$config->growable = false;
|
||||
$config->prompt = 'backend::lang.list.search_prompt';
|
||||
$searchWidget = $controller->makeWidget('Backend\Widgets\Search', $config);
|
||||
$searchWidget->cssClasses[] = 'condition-filter-search';
|
||||
|
||||
$listWidget->bindToController();
|
||||
$searchWidget->bindToController();
|
||||
|
||||
/*
|
||||
* Extend list query
|
||||
*/
|
||||
$listWidget->bindEvent('list.extendQueryBefore', function ($query) use ($filterModel) {
|
||||
$this->prepareFilterQuery($query, $filterModel);
|
||||
});
|
||||
|
||||
/*
|
||||
* Link the Search Widget to the List Widget
|
||||
*/
|
||||
$listWidget->setSearchTerm($searchWidget->getActiveTerm());
|
||||
|
||||
$searchWidget->bindEvent('search.submit', function () use (&$searchWidget, $listWidget) {
|
||||
$listWidget->setSearchTerm($searchWidget->getActiveTerm());
|
||||
return $listWidget->onRefresh();
|
||||
});
|
||||
|
||||
$controller->vars['listWidget'] = $listWidget;
|
||||
$controller->vars['searchWidget'] = $searchWidget;
|
||||
$controller->vars['filterHostModel'] = $this->host;
|
||||
}
|
||||
|
||||
public function prepareFilterQuery($query, $model)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Condition check
|
||||
//
|
||||
|
||||
/**
|
||||
* Checks whether the condition is TRUE for a specified model
|
||||
* @return bool
|
||||
*/
|
||||
public function evalIsTrue($model, $customValue = '__no_eval_value__')
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
|
||||
$operator = $hostObj->operator;
|
||||
$attribute = $hostObj->subcondition;
|
||||
|
||||
$conditionValue = $hostObj->value;
|
||||
$conditionValue = trim(mb_strtolower($conditionValue));
|
||||
|
||||
$controlType = $this->getValueControlType();
|
||||
|
||||
if ($controlType == 'text') {
|
||||
if ($customValue === self::NO_EVAL_VALUE) {
|
||||
$modelValue = trim(mb_strtolower($model->{$attribute}));
|
||||
}
|
||||
else {
|
||||
$modelValue = trim(mb_strtolower($customValue));
|
||||
}
|
||||
|
||||
if ($operator == 'is') {
|
||||
return $modelValue == $conditionValue;
|
||||
}
|
||||
|
||||
if ($operator == 'is_not') {
|
||||
return $modelValue != $conditionValue;
|
||||
}
|
||||
|
||||
if ($operator == 'contains') {
|
||||
return mb_strpos($modelValue, $conditionValue) !== false;
|
||||
}
|
||||
|
||||
if ($operator == 'does_not_contain') {
|
||||
return mb_strpos($modelValue, $conditionValue) === false;
|
||||
}
|
||||
|
||||
if ($operator == 'equals_or_greater') {
|
||||
return $modelValue >= $conditionValue;
|
||||
}
|
||||
|
||||
if ($operator == 'equals_or_less') {
|
||||
return $modelValue <= $conditionValue;
|
||||
}
|
||||
|
||||
if ($operator == 'greater') {
|
||||
return $modelValue > $conditionValue;
|
||||
}
|
||||
|
||||
if ($operator == 'less') {
|
||||
return $modelValue < $conditionValue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($controlType == 'dropdown') {
|
||||
if ($customValue === self::NO_EVAL_VALUE) {
|
||||
$modelValue = $model->{$attribute};
|
||||
}
|
||||
else {
|
||||
$modelValue = $customValue;
|
||||
}
|
||||
|
||||
if ($operator == 'is') {
|
||||
if ($modelValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($modelValue instanceof EloquentModel) {
|
||||
return $modelValue->getKey() == $conditionValue;
|
||||
}
|
||||
|
||||
if (
|
||||
is_array($modelValue) &&
|
||||
count($modelValue) == 1 &&
|
||||
array_key_exists(0, $modelValue)
|
||||
) {
|
||||
return $modelValue[0] == $conditionValue;
|
||||
}
|
||||
|
||||
if ($modelValue instanceof EloquentCollection) {
|
||||
if ($modelValue->count() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $modelValue[0]->getKey() == $conditionValue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($operator == 'is_not') {
|
||||
if ($modelValue == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($modelValue instanceof EloquentModel) {
|
||||
return $modelValue->getKey() != $conditionValue;
|
||||
}
|
||||
|
||||
if (is_array($modelValue)) {
|
||||
if (count($modelValue) != 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!array_key_exists(0, $modelValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $modelValue[0] != $conditionValue;
|
||||
}
|
||||
|
||||
if ($modelValue instanceof EloquentCollection) {
|
||||
if (!$modelValue->count() || $modelValue->count() > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $modelValue->first()->getKey() != $conditionValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($controlType == 'multi_value') {
|
||||
if ($customValue === self::NO_EVAL_VALUE) {
|
||||
$modelValue = $model->{$attribute};
|
||||
}
|
||||
else {
|
||||
$modelValue = $customValue;
|
||||
}
|
||||
|
||||
if (
|
||||
(!$modelValue instanceof EloquentCollection) &&
|
||||
(!$modelValue instanceof EloquentModel) &&
|
||||
!is_array($modelValue)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen($conditionValue)) {
|
||||
$conditionValues = explode(',', $conditionValue);
|
||||
foreach ($conditionValues as &$value) {
|
||||
$value = trim($value);
|
||||
}
|
||||
} else {
|
||||
$conditionValues = [];
|
||||
}
|
||||
|
||||
if ($modelValue instanceof EloquentCollection) {
|
||||
$modelKeys = array_keys($modelValue->lists('id', 'id'));
|
||||
}
|
||||
elseif ($modelValue instanceof EloquentModel) {
|
||||
$modelKeys = [$modelValue->getKey()];
|
||||
}
|
||||
else {
|
||||
$modelKeys = $modelValue;
|
||||
}
|
||||
|
||||
if ($operator == 'is') {
|
||||
$operator = 'one_of';
|
||||
}
|
||||
elseif ($operator == 'is_not') {
|
||||
$operator = 'not_one_of';
|
||||
}
|
||||
|
||||
if ($operator == 'one_of') {
|
||||
return count(array_intersect($conditionValues, $modelKeys)) ? true : false;
|
||||
}
|
||||
|
||||
if ($operator == 'not_one_of') {
|
||||
return count(array_intersect($conditionValues, $modelKeys)) ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
public function getModelObj()
|
||||
{
|
||||
if ($this->modelObj === null) {
|
||||
if (array_key_exists($this->modelClass, self::$modelObjCache)) {
|
||||
$this->modelObj = self::$modelObjCache[$this->modelClass];
|
||||
}
|
||||
else {
|
||||
$this->modelObj = self::$modelObjCache[$this->modelClass] = new $this->modelClass;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->modelObj;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use App;
|
||||
use Queue;
|
||||
use Event;
|
||||
use BackendAuth;
|
||||
use System\Classes\PluginManager;
|
||||
use RainLab\Notify\Models\NotificationRule as NotificationRuleModel;
|
||||
|
||||
/**
|
||||
* Notification manager
|
||||
*
|
||||
* @package rainlab\notify
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Notifier
|
||||
{
|
||||
use \October\Rain\Support\Traits\Singleton;
|
||||
|
||||
/**
|
||||
* @var array Cache of registration callbacks.
|
||||
*/
|
||||
protected $callbacks = [];
|
||||
|
||||
/**
|
||||
* @var bool Internal marker to see if callbacks are run.
|
||||
*/
|
||||
protected $registered = false;
|
||||
|
||||
/**
|
||||
* @var array List of registered global params in the system
|
||||
*/
|
||||
protected $registeredGlobalParams;
|
||||
|
||||
/**
|
||||
* Registers a callback function that defines context variables.
|
||||
* The callback function should register context variables by calling the manager's
|
||||
* `registerGlobalParams` method. The manager instance is passed to the callback
|
||||
* function as an argument. Usage:
|
||||
*
|
||||
* Notifier::registerCallback(function($manager){
|
||||
* $manager->registerGlobalParams([...]);
|
||||
* });
|
||||
*
|
||||
* @param callable $callback A callable function.
|
||||
*/
|
||||
public function registerCallback(callable $callback)
|
||||
{
|
||||
$this->callbacks[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to process callbacks once and once only.
|
||||
* @return void
|
||||
*/
|
||||
protected function processCallbacks()
|
||||
{
|
||||
if ($this->registered) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->callbacks as $callback) {
|
||||
$callback($this);
|
||||
}
|
||||
|
||||
$this->registered = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Event binding
|
||||
//
|
||||
|
||||
public static function bindEvents(array $events)
|
||||
{
|
||||
foreach ($events as $event => $class) {
|
||||
self::bindEvent($event, $class);
|
||||
}
|
||||
}
|
||||
|
||||
public static function bindEvent($systemEventName, $notifyEventClass)
|
||||
{
|
||||
Event::listen($systemEventName, function() use ($notifyEventClass, $systemEventName) {
|
||||
$params = $notifyEventClass::makeParamsFromEvent(func_get_args(), $systemEventName);
|
||||
|
||||
self::instance()->queueEvent($notifyEventClass, $params);
|
||||
});
|
||||
}
|
||||
|
||||
public function queueEvent($eventClass, array $params)
|
||||
{
|
||||
$params += $this->getContextVars();
|
||||
|
||||
// Use queue
|
||||
if (true) {
|
||||
Queue::push(new EventParams($eventClass, $params));
|
||||
}
|
||||
else {
|
||||
$this->fireEvent($eventClass, $params);
|
||||
}
|
||||
}
|
||||
|
||||
public function fireEvent($eventClass, array $params)
|
||||
{
|
||||
$models = NotificationRuleModel::listRulesForEvent($eventClass);
|
||||
|
||||
foreach ($models as $model) {
|
||||
$model->setParams($params);
|
||||
$model->triggerRule();
|
||||
}
|
||||
}
|
||||
|
||||
public function registerGlobalParams(array $params)
|
||||
{
|
||||
if (!$this->registeredGlobalParams) {
|
||||
$this->registeredGlobalParams = [];
|
||||
}
|
||||
|
||||
$this->registeredGlobalParams = $params + $this->registeredGlobalParams;
|
||||
}
|
||||
|
||||
public function getContextVars()
|
||||
{
|
||||
$this->processCallbacks();
|
||||
|
||||
$globals = $this->registeredGlobalParams ?: [];
|
||||
|
||||
return [
|
||||
'isBackend' => App::runningInBackend() ? 1 : 0,
|
||||
'isConsole' => App::runningInConsole() ? 1 : 0,
|
||||
'appLocale' => App::getLocale(),
|
||||
'sender' => null // unsafe:BackendAuth::getUser()
|
||||
] + $globals;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php namespace RainLab\Notify\Classes;
|
||||
|
||||
use Rainlab\Notify\Models\RuleAction;
|
||||
|
||||
class ScheduledAction
|
||||
{
|
||||
use \Illuminate\Queue\InteractsWithQueue;
|
||||
use \Illuminate\Queue\SerializesAndRestoresModelIdentifiers;
|
||||
|
||||
protected $action;
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($action, array $params)
|
||||
{
|
||||
$this->action = $action->id;
|
||||
$this->params = $this->serializeParams($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->delete();
|
||||
|
||||
if (! $actionClass = RuleAction::find($this->action)) {
|
||||
return traceLog('Error: Could not restore action #' . $this->action);
|
||||
}
|
||||
|
||||
$params = $this->unserializeParams();
|
||||
$actionClass->triggerAction($params, false);
|
||||
}
|
||||
|
||||
protected function serializeParams($params)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
$result[$param] = $this->getSerializedPropertyValue($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function unserializeParams()
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->params as $param => $value) {
|
||||
$result[$param] = $this->getRestoredPropertyValue($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
condition_type:
|
||||
label: Condition Type
|
||||
span: auto
|
||||
type: dropdown
|
||||
|
||||
condition:
|
||||
label: Required Value
|
||||
span: auto
|
||||
type: dropdown
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<a
|
||||
href="javascript:;"
|
||||
class="nolink"
|
||||
data-record-key="<?= e($record->getKey()) ?>"
|
||||
data-record-value="<?= e($filterHostModel->getReferencePrimaryColumn($record)) ?>"
|
||||
data-multivalue-add-record>
|
||||
<span class="list-badge badge-success">
|
||||
<i class="icon-plus"></i>
|
||||
</span>
|
||||
</a>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
$attribute = $formModel->subcondition;
|
||||
$controlType = $formModel->getValueControlType();
|
||||
?>
|
||||
<?php if ($controlType == 'text'): ?>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
maxlength="255"
|
||||
/>
|
||||
|
||||
<?php elseif ($controlType == 'dropdown'): ?>
|
||||
<?php
|
||||
$fieldOptions = $formModel->getValueDropdownOptions();
|
||||
?>
|
||||
<select
|
||||
id="<?= $field->getId() ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
class="form-control custom-select">
|
||||
<?php foreach ($fieldOptions as $value => $option): ?>
|
||||
<option
|
||||
<?= $field->isSelected($value) ? 'selected="selected"' : '' ?>
|
||||
value="<?= $value ?>"><?= e(trans($option)) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
|
||||
<?php elseif ($controlType == 'multi_value'): ?>
|
||||
<?php
|
||||
$selectedRecords = $formModel->listSelectedReferenceRecords();
|
||||
$hasData = $selectedRecords->count();
|
||||
?>
|
||||
<div
|
||||
class="condition-multi-value"
|
||||
data-control="condition-multivalue"
|
||||
data-data-locker="#<?= $field->getId() ?>">
|
||||
|
||||
<div class="filter-list list-flush">
|
||||
<?= $searchWidget->render() ?>
|
||||
<?= $listWidget->render() ?>
|
||||
</div>
|
||||
|
||||
<label class="form-label"><?= e(__('Selected Records')) ?></label>
|
||||
<div class="added-filter-list list-preview">
|
||||
<div class="control-list">
|
||||
<table class="table data">
|
||||
<tbody data-added-records>
|
||||
<?php if ($hasData): ?>
|
||||
<?php foreach ($selectedRecords as $record): ?>
|
||||
<tr data-record-key="<?= e($record->getKey()) ?>">
|
||||
<td style="width: 10px">
|
||||
<a href="javascript:;" data-multivalue-remove-record>
|
||||
<span class="list-badge badge-default">
|
||||
<i class="icon-minus"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<?= e($formModel->getReferencePrimaryColumn($record)) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
/>
|
||||
|
||||
<script type="text/template" data-empty-record-template>
|
||||
<tr class="no-data" data-no-record-data>
|
||||
<td colspan="100" class="nolink">
|
||||
<p class="no-data">
|
||||
<?= e(__('No records added')) ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-record-template>
|
||||
<tr data-record-key="{{ key }}">
|
||||
<td style="width: 10px">
|
||||
<a href="javascript:;" data-multivalue-remove-record>
|
||||
<span class="list-badge badge-default">
|
||||
<i class="icon-minus"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{value}}
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
subcondition:
|
||||
label: Attribute
|
||||
span: auto
|
||||
type: dropdown
|
||||
|
||||
operator:
|
||||
label: Operator
|
||||
span: auto
|
||||
type: dropdown
|
||||
dependsOn: subcondition
|
||||
|
||||
value:
|
||||
label: Value
|
||||
dependsOn: [subcondition, operator]
|
||||
type: partial
|
||||
path: field_value
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "rainlab/notify-plugin",
|
||||
"type": "october-plugin",
|
||||
"description": "Notify plugin for October CMS",
|
||||
"homepage": "https://octobercms.com/plugin/rainlab-pages",
|
||||
"keywords": ["october", "octobercms", "pages"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alexey Bobkov",
|
||||
"email": "aleksey.bobkov@gmail.com",
|
||||
"role": "Co-founder"
|
||||
},
|
||||
{
|
||||
"name": "Samuel Georges",
|
||||
"email": "daftspunky@gmail.com",
|
||||
"role": "Co-founder"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"composer/installers": "~1.0"
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
<?php namespace RainLab\Notify\Controllers;
|
||||
|
||||
use BackendMenu;
|
||||
use Backend\Classes\Controller;
|
||||
use RainLab\Notify\Models\NotificationRule;
|
||||
use System\Classes\SettingsManager;
|
||||
use RainLab\Notify\Classes\EventBase;
|
||||
use ApplicationException;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Notification rules controller
|
||||
*
|
||||
* @package rainlab\notify
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Notifications extends Controller
|
||||
{
|
||||
public $implement = [
|
||||
\Backend\Behaviors\FormController::class,
|
||||
\Backend\Behaviors\ListController::class
|
||||
];
|
||||
|
||||
public $requiredPermissions = ['rainlab.notify.manage_notifications'];
|
||||
|
||||
public $listConfig = 'config_list.yaml';
|
||||
public $formConfig = 'config_form.yaml';
|
||||
|
||||
public $eventAlias;
|
||||
protected $eventClass;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
BackendMenu::setContext('October.System', 'system', 'settings');
|
||||
SettingsManager::setContext('RainLab.Notify', 'notifications');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
NotificationRule::syncAll();
|
||||
$this->asExtension('ListController')->index();
|
||||
}
|
||||
|
||||
public function create($eventAlias = null)
|
||||
{
|
||||
try {
|
||||
if (!$eventAlias) {
|
||||
throw new ApplicationException('Missing a rule code');
|
||||
}
|
||||
|
||||
$this->eventAlias = $eventAlias;
|
||||
$this->bodyClass = 'compact-container breadcrumb-fancy';
|
||||
$this->asExtension('FormController')->create();
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
public function update($recordId = null, $context = null)
|
||||
{
|
||||
$this->bodyClass = 'compact-container breadcrumb-fancy';
|
||||
$this->asExtension('FormController')->update($recordId, $context);
|
||||
}
|
||||
|
||||
public function formExtendModel($model)
|
||||
{
|
||||
if (!$model->exists) {
|
||||
$model->applyEventClass($this->getEventClass());
|
||||
$model->name = $model->getEventDescription();
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
// public function formBeforeSave($model)
|
||||
// {
|
||||
// $model->is_custom = 1;
|
||||
// }
|
||||
|
||||
public function index_onLoadRuleGroupForm()
|
||||
{
|
||||
try {
|
||||
$groups = EventBase::findEventGroups();
|
||||
$this->vars['eventGroups'] = $groups;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->makePartial('add_rule_group_form');
|
||||
}
|
||||
|
||||
/**
|
||||
* This handler requires the group code passed from `onLoadRuleGroupForm`
|
||||
*/
|
||||
public function index_onLoadRuleEventForm()
|
||||
{
|
||||
try {
|
||||
if (!$code = post('code')) {
|
||||
throw new ApplicationException('Missing event group code');
|
||||
}
|
||||
|
||||
$events = EventBase::findEventsByGroup($code);
|
||||
$this->vars['events'] = $events;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->makePartial('add_rule_event_form');
|
||||
}
|
||||
|
||||
protected function getEventClass()
|
||||
{
|
||||
$alias = post('event_alias', $this->eventAlias);
|
||||
|
||||
if ($this->eventClass !== null) {
|
||||
return $this->eventClass;
|
||||
}
|
||||
|
||||
if (!$event = EventBase::findEventByIdentifier($alias)) {
|
||||
throw new ApplicationException('Unable to find event with alias: '. $alias);
|
||||
}
|
||||
|
||||
return $this->eventClass = get_class($event);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?= Form::open(['id' => 'addRuleEventForm']) ?>
|
||||
<div class="modal-header flex-row-reverse">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(__('Add Notification Rule')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<?php if ($this->fatalError): ?>
|
||||
<p class="flash-message static error"><?= $fatalError ?></p>
|
||||
<?php else: ?>
|
||||
|
||||
<div class="control-simplelist is-selectable is-scrollable size-large" data-control="simplelist">
|
||||
<ul>
|
||||
<?php foreach ($events as $event): ?>
|
||||
|
||||
<li>
|
||||
<a href="<?= Backend::url('rainlab/notify/notifications/create/'.$event->getEventIdentifier()) ?>">
|
||||
<h5 class="heading"><?= e(trans($event->getEventName())) ?></h5>
|
||||
<p class="description"><?= e(trans($event->getEventDescription())) ?></p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?= Form::open(['id' => 'addRuleGroupForm']) ?>
|
||||
<div class="modal-header flex-row-reverse">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(__('Add Notification Rule')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<?php if ($this->fatalError): ?>
|
||||
<p class="flash-message static error"><?= $fatalError ?></p>
|
||||
<?php else: ?>
|
||||
|
||||
<div class="control-simplelist is-selectable-box is-scrollable size-large" data-control="simplelist">
|
||||
<ul>
|
||||
<?php foreach ($eventGroups as $code => $group): ?>
|
||||
|
||||
<li>
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-control="popup"
|
||||
data-handler="onLoadRuleEventForm"
|
||||
data-request-data="code: '<?= e($code) ?>'"
|
||||
data-dismiss="popup"
|
||||
>
|
||||
<div class="box">
|
||||
<div class="image"><i class="<?= e(array_get($group, 'icon')) ?>"></i></div>
|
||||
</div>
|
||||
<h5 class="heading"><?= e(trans(array_get($group, 'label'))) ?></h5>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
$isCreate = $this->formGetContext() == 'create';
|
||||
$pageUrl = isset($pageUrl) ? $pageUrl : null;
|
||||
?>
|
||||
<div class="form-buttons loading-indicator-container">
|
||||
<!-- Save -->
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary oc-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-request-before-update="$(this).trigger('unchange.oc.changeMonitor')"
|
||||
<?php if (!$isCreate): ?>data-request-data="redirect:0"<?php endif ?>
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<?php if (!$isCreate): ?>
|
||||
<!-- Save and Close -->
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary oc-icon-check save"
|
||||
data-request-before-update="$(this).trigger('unchange.oc.changeMonitor')"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>">
|
||||
<?= e(trans('backend::lang.form.save_and_close')) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (!$isCreate): ?>
|
||||
<!-- Delete -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default empty oc-icon-trash-o"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(__('Delete this notification rule?')) ?>"
|
||||
data-control="delete-button"></button>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<div data-control="toolbar">
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-control="popup"
|
||||
data-handler="onLoadRuleGroupForm"
|
||||
class="btn btn-primary oc-icon-plus">
|
||||
<?= e(__('New Notification Rule')) ?>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# ===================================
|
||||
# Form Behavior Config
|
||||
# ===================================
|
||||
|
||||
name: Notification Rule
|
||||
form: $/rainlab/notify/models/notificationrule/fields.yaml
|
||||
modelClass: RainLab\Notify\Models\NotificationRule
|
||||
defaultRedirect: rainlab/notify/notifications
|
||||
|
||||
create:
|
||||
redirect: rainlab/notify/notifications/update/:id
|
||||
redirectClose: rainlab/notify/notifications
|
||||
|
||||
update:
|
||||
redirect: rainlab/notify/notifications
|
||||
redirectClose: rainlab/notify/notifications
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# ===================================
|
||||
# List Behavior Config
|
||||
# ===================================
|
||||
|
||||
title: Notification Rules
|
||||
list: $/rainlab/notify/models/notificationrule/columns.yaml
|
||||
modelClass: RainLab\Notify\Models\NotificationRule
|
||||
recordUrl: rainlab/notify/notifications/update/:id
|
||||
noRecordsMessage: backend::lang.list.no_records
|
||||
recordsPerPage: 30
|
||||
showSetup: true
|
||||
showCheckboxes: true
|
||||
defaultSort:
|
||||
column: count
|
||||
direction: desc
|
||||
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
search:
|
||||
prompt: backend::lang.list.search_prompt
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('rainlab/notify/notifications') ?>"><?= e(__('Notification Rules')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="layout fancy-layout">
|
||||
<?= Form::open([
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(__('Are you sure?')),
|
||||
'id' => 'post-form'
|
||||
]) ?>
|
||||
<input type="hidden" name="event_alias" value="<?= $this->eventAlias ?>" />
|
||||
|
||||
<?= $this->formRender() ?>
|
||||
|
||||
<?= Form::close() ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="padded-container">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('rainlab/notify/notifications') ?>" class="btn btn-default"><?= e(__('Return to Notifications')) ?></a></p>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<?= $this->listRender() ?>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('rainlab/notify/notifications') ?>"><?= e(__('Notification Rules')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="layout fancy-layout">
|
||||
<?= Form::open([
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(__('Are you sure?')),
|
||||
'id' => 'post-form'
|
||||
]) ?>
|
||||
|
||||
<?= $this->formRender() ?>
|
||||
|
||||
<?= Form::close() ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="padded-container">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('rainlab/notify/notifications') ?>" class="btn btn-default"><?= e(__('Return to Notifications')) ?></a></p>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,388 @@
|
|||
<?php namespace RainLab\Notify\FormWidgets;
|
||||
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
use RainLab\Notify\Classes\ActionBase;
|
||||
use ApplicationException;
|
||||
use ValidationException;
|
||||
use Exception;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Action builder
|
||||
*/
|
||||
class ActionBuilder extends FormWidgetBase
|
||||
{
|
||||
use \Backend\Traits\FormModelWidget;
|
||||
|
||||
//
|
||||
// Configurable properties
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// Object properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @var mixed Actions cache
|
||||
*/
|
||||
protected $actionsCache = false;
|
||||
|
||||
/**
|
||||
* @var Backend\Widgets\Form
|
||||
*/
|
||||
protected $actionFormWidget;
|
||||
|
||||
/**
|
||||
* @var Backend\Widgets\Form
|
||||
*/
|
||||
protected $actionScheduleFormWidget;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// $this->fillFromConfig([
|
||||
// ]);
|
||||
|
||||
if ($widget = $this->makeActionFormWidget()) {
|
||||
$widget->bindToController();
|
||||
}
|
||||
|
||||
if ($widget = $this->makeActionScheduleFormWidget()) {
|
||||
$widget->bindToController();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addJs('js/actions.js', 'RainLab.Notify');
|
||||
$this->addCss('css/actions.css', 'RainLab.Notify');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
|
||||
return $this->makePartial('actions_container');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the list data
|
||||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['name'] = $this->getFieldName();
|
||||
$this->vars['formModel'] = $this->model;
|
||||
$this->vars['actions'] = $this->getActions();
|
||||
$this->vars['actionFormWidget'] = $this->actionFormWidget;
|
||||
$connection = config('queue.default');
|
||||
$this->vars['queueDriver'] = config("queue.connections.{$connection}.driver");
|
||||
$this->vars['actionScheduleFormWidget'] = $this->actionScheduleFormWidget;
|
||||
$this->vars['availableTags'] = $this->getAvailableTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
$this->model->bindEvent('model.afterSave', function() {
|
||||
$this->processSave();
|
||||
});
|
||||
|
||||
return FormField::NO_SAVE_DATA;
|
||||
}
|
||||
|
||||
protected function processSave()
|
||||
{
|
||||
$cache = $this->getCacheActionDataPayload();
|
||||
|
||||
foreach ($cache as $id => $data) {
|
||||
$action = $this->findActionObj($id);
|
||||
|
||||
if ($attributes = $this->getCacheActionAttributes($action)) {
|
||||
$action->fill($attributes);
|
||||
}
|
||||
|
||||
$action->save(null, $this->sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX
|
||||
//
|
||||
|
||||
public function onLoadCreateActionForm()
|
||||
{
|
||||
try {
|
||||
$actions = ActionBase::findActions();
|
||||
$this->vars['actions'] = $actions;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->makePartial('create_action_form');
|
||||
}
|
||||
|
||||
public function onSaveAction()
|
||||
{
|
||||
$this->restoreCacheActionDataPayload();
|
||||
|
||||
$action = $this->findActionObj();
|
||||
|
||||
$data = post('Action', []);
|
||||
$schedule = post('ActionSchedule', []);
|
||||
$action->fill($data);
|
||||
$action->fill($schedule);
|
||||
$action->validate();
|
||||
$action->action_text = $action->getActionObject()->getText();
|
||||
|
||||
$action->applyCustomData();
|
||||
|
||||
$this->setCacheActionData($action);
|
||||
|
||||
return $this->renderActions($action);
|
||||
}
|
||||
|
||||
public function onLoadActionSetup()
|
||||
{
|
||||
try {
|
||||
$action = $this->findActionObj();
|
||||
|
||||
$data = $this->getCacheActionAttributes($action);
|
||||
|
||||
if (!is_null($this->actionFormWidget)) {
|
||||
$this->actionFormWidget->setFormValues($data);
|
||||
}
|
||||
$this->actionScheduleFormWidget->setFormValues($data);
|
||||
|
||||
$this->prepareVars();
|
||||
$this->vars['action'] = $action;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->makePartial('action_settings_form');
|
||||
}
|
||||
|
||||
public function onCreateAction()
|
||||
{
|
||||
if (!$className = post('action_class')) {
|
||||
throw new ApplicationException('Please specify an action');
|
||||
}
|
||||
|
||||
$this->restoreCacheActionDataPayload();
|
||||
|
||||
$newAction = $this->getRelationModel();
|
||||
$newAction->class_name = $className;
|
||||
$newAction->save();
|
||||
|
||||
$this->model->rule_actions()->add($newAction, post('_session_key'));
|
||||
|
||||
$this->vars['newActionId'] = $newAction->id;
|
||||
|
||||
return $this->renderActions();
|
||||
}
|
||||
|
||||
public function onDeleteAction()
|
||||
{
|
||||
$action = $this->findActionObj();
|
||||
|
||||
$this->model->rule_actions()->remove($action, post('_session_key'));
|
||||
|
||||
return $this->renderActions();
|
||||
}
|
||||
|
||||
public function onCancelActionSettings()
|
||||
{
|
||||
$action = $this->findActionObj(post('new_action_id'));
|
||||
|
||||
$action->delete();
|
||||
|
||||
return $this->renderActions();
|
||||
}
|
||||
|
||||
//
|
||||
// Postback deferring
|
||||
//
|
||||
|
||||
public function getCacheActionAttributes($action)
|
||||
{
|
||||
return array_get($this->getCacheActionData($action), 'attributes');
|
||||
}
|
||||
|
||||
public function getCacheActionTitle($action)
|
||||
{
|
||||
return array_get($this->getCacheActionData($action), 'title');
|
||||
}
|
||||
|
||||
public function getCacheActionText($action)
|
||||
{
|
||||
return array_get($this->getCacheActionData($action), 'text');
|
||||
}
|
||||
|
||||
public function getCacheActionData($action, $default = null)
|
||||
{
|
||||
$cache = post('action_data', []);
|
||||
|
||||
if (is_array($cache) && array_key_exists($action->id, $cache)) {
|
||||
return json_decode($cache[$action->id], true);
|
||||
}
|
||||
|
||||
if ($default === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->makeCacheActionData($action);
|
||||
}
|
||||
|
||||
public function makeCacheActionData($action)
|
||||
{
|
||||
$data = [
|
||||
'attributes' => $action->config_data,
|
||||
'title' => $action->getTitle(),
|
||||
'text' => $action->getText(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function setCacheActionData($action)
|
||||
{
|
||||
$cache = post('action_data', []);
|
||||
|
||||
$cache[$action->id] = json_encode($this->makeCacheActionData($action));
|
||||
|
||||
Request::merge([
|
||||
'action_data' => $cache
|
||||
]);
|
||||
}
|
||||
|
||||
public function restoreCacheActionDataPayload()
|
||||
{
|
||||
Request::merge([
|
||||
'action_data' => json_decode(post('current_action_data'), true)
|
||||
]);
|
||||
}
|
||||
|
||||
public function getCacheActionDataPayload()
|
||||
{
|
||||
return post('action_data', []);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function getAvailableTags()
|
||||
{
|
||||
$tags = [];
|
||||
|
||||
if ($this->model->methodExists('defineParams')) {
|
||||
$params = $this->model->defineParams();
|
||||
|
||||
foreach ($params as $param => $definition) {
|
||||
$tags[$param] = array_get($definition, 'label');
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the primary rule actions container
|
||||
* @return array
|
||||
*/
|
||||
protected function renderActions()
|
||||
{
|
||||
$this->prepareVars();
|
||||
|
||||
return [
|
||||
'#'.$this->getId() => $this->makePartial('actions')
|
||||
];
|
||||
}
|
||||
|
||||
protected function makeActionFormWidget()
|
||||
{
|
||||
if ($this->actionFormWidget !== null) {
|
||||
return $this->actionFormWidget;
|
||||
}
|
||||
|
||||
if (!$model = $this->findActionObj(null, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$model->hasFieldConfig()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$config = $model->getFieldConfig();
|
||||
$config->model = $model;
|
||||
$config->alias = $this->alias . 'Form';
|
||||
$config->arrayName = 'Action';
|
||||
|
||||
$widget = $this->makeWidget('Backend\Widgets\Form', $config);
|
||||
|
||||
return $this->actionFormWidget = $widget;
|
||||
}
|
||||
|
||||
protected function makeActionScheduleFormWidget()
|
||||
{
|
||||
if ($this->actionScheduleFormWidget !== null) {
|
||||
return $this->actionScheduleFormWidget;
|
||||
}
|
||||
|
||||
if (!$model = $this->findActionObj(null, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$config = $this->makeConfig('$/rainlab/notify/models/ruleaction/fields_schedule.yaml');
|
||||
$config->model = $model;
|
||||
$config->alias = $this->alias . 'ScheduleForm';
|
||||
$config->arrayName = 'ActionSchedule';
|
||||
|
||||
$widget = $this->makeWidget('Backend\Widgets\Form', $config);
|
||||
|
||||
return $this->actionScheduleFormWidget = $widget;
|
||||
}
|
||||
|
||||
protected function getActions()
|
||||
{
|
||||
if ($this->actionsCache !== false) {
|
||||
return $this->actionsCache;
|
||||
}
|
||||
|
||||
$relationObject = $this->getRelationObject();
|
||||
$actions = $relationObject->withDeferred($this->sessionKey)->get();
|
||||
|
||||
return $this->actionsCache = $actions ?: null;
|
||||
}
|
||||
|
||||
protected function findActionObj($actionId = null, $throw = true)
|
||||
{
|
||||
$actionId = $actionId ? $actionId : post('current_action_id');
|
||||
|
||||
$action = null;
|
||||
|
||||
if (strlen($actionId)) {
|
||||
$action = $this->getRelationModel()->find($actionId);
|
||||
}
|
||||
|
||||
if ($throw && !$action) {
|
||||
throw new ApplicationException('Action not found');
|
||||
}
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,455 @@
|
|||
<?php namespace RainLab\Notify\FormWidgets;
|
||||
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
use RainLab\Notify\Classes\ConditionBase;
|
||||
use ApplicationException;
|
||||
use ValidationException;
|
||||
use Exception;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Condition builder
|
||||
*/
|
||||
class ConditionBuilder extends FormWidgetBase
|
||||
{
|
||||
use \Backend\Traits\FormModelWidget;
|
||||
use \Backend\Traits\CollapsableWidget;
|
||||
|
||||
//
|
||||
// Configurable properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @var string Rule type.
|
||||
*/
|
||||
public $conditionsRuleType = ConditionBase::TYPE_ANY;
|
||||
|
||||
//
|
||||
// Object properties
|
||||
//
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public $defaultAlias = 'ruleconditions';
|
||||
|
||||
/**
|
||||
* @var mixed Root condition
|
||||
*/
|
||||
protected $conditionsRoot = false;
|
||||
|
||||
/**
|
||||
* @var Backend\Widgets\Form
|
||||
*/
|
||||
protected $conditionFormWidget;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->fillFromConfig([
|
||||
'conditionsRuleType',
|
||||
]);
|
||||
|
||||
if ($widget = $this->makeConditionFormWidget()) {
|
||||
$widget->bindToController();
|
||||
}
|
||||
|
||||
$this->initRootCondition();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addJs('js/conditions.js', 'RainLab.Notify');
|
||||
$this->addJs('js/conditions.multivalue.js', 'RainLab.Notify');
|
||||
$this->addCss('css/conditions.css', 'RainLab.Notify');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
|
||||
return $this->makePartial('conditions_container');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the list data
|
||||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['name'] = $this->getFieldName();
|
||||
$this->vars['rootCondition'] = $this->getConditionsRoot();
|
||||
$this->vars['conditionFormWidget'] = $this->conditionFormWidget;
|
||||
}
|
||||
|
||||
public function initRootCondition()
|
||||
{
|
||||
if ($this->getConditionsRoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relationObject = $this->getRelationObject();
|
||||
|
||||
$rootRule = $this->getRelationModel();
|
||||
$rootRule->rule_host_type = $this->conditionsRuleType;
|
||||
$rootRule->class_name = $rootRule->getRootConditionClass();
|
||||
$rootRule->save();
|
||||
|
||||
$relationObject->add($rootRule, $this->sessionKey);
|
||||
|
||||
$this->conditionsRoot = $rootRule;
|
||||
}
|
||||
|
||||
public function isRootCondition($condition)
|
||||
{
|
||||
if ($root = $this->getConditionsRoot()) {
|
||||
return $condition->id === $root->id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getConditionsRoot()
|
||||
{
|
||||
if ($this->conditionsRoot !== false) {
|
||||
return $this->conditionsRoot;
|
||||
}
|
||||
|
||||
$relationObject = $this->getRelationObject();
|
||||
$rootCondition = $relationObject->withDeferred($this->sessionKey)->first();
|
||||
|
||||
return $this->conditionsRoot = $rootCondition ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
$this->model->bindEvent('model.afterSave', function() {
|
||||
$this->processSave();
|
||||
});
|
||||
|
||||
return FormField::NO_SAVE_DATA;
|
||||
}
|
||||
|
||||
protected function processSave()
|
||||
{
|
||||
$cache = $this->getCacheConditionDataPayload();
|
||||
|
||||
foreach ($cache as $id => $data) {
|
||||
$condition = $this->findConditionObj($id);
|
||||
$attributes = $this->getCacheConditionAttributes($condition);
|
||||
$condition->fill($attributes);
|
||||
$condition->save(null, $this->sessionKey.'_'.$condition->id);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX
|
||||
//
|
||||
|
||||
public function onLoadConditionSetup()
|
||||
{
|
||||
try {
|
||||
$condition = $this->findConditionObj();
|
||||
|
||||
$this->prepareVars();
|
||||
|
||||
$this->vars['condition'] = $condition;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->makePartial('condition_settings_form');
|
||||
}
|
||||
|
||||
public function onLoadCreateChildCondition()
|
||||
{
|
||||
try {
|
||||
$condition = $this->findConditionObj();
|
||||
|
||||
/*
|
||||
* Look up parents
|
||||
*/
|
||||
$parents = [$condition->id];
|
||||
$parentsArray = post('condition_parent_id', []);
|
||||
$currentId = $condition->id;
|
||||
|
||||
while (array_key_exists($currentId, $parentsArray) && $parentsArray[$currentId]) {
|
||||
$parents[] = $currentId = $parentsArray[$currentId];
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom rules provided by model
|
||||
*/
|
||||
$extraRules = [];
|
||||
if ($this->model->methodExists('getExtraConditionRules')) {
|
||||
$extraRules = $this->model->getExtraConditionRules();
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up conditions
|
||||
*/
|
||||
$options = $condition->getChildOptions([
|
||||
'ruleType' => $this->conditionsRuleType,
|
||||
'parentIds' => $parents,
|
||||
'extraRules' => $extraRules
|
||||
]);
|
||||
|
||||
$this->prepareVars();
|
||||
$this->vars['condition'] = $condition;
|
||||
$this->vars['options'] = $options;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->makePartial('create_child_form');
|
||||
}
|
||||
|
||||
public function onSaveCondition()
|
||||
{
|
||||
$this->restoreCacheConditionDataPayload();
|
||||
|
||||
$condition = $this->findConditionObj();
|
||||
|
||||
$data = post('Condition', []);
|
||||
$condition->fill($data);
|
||||
$condition->validate();
|
||||
$condition->condition_text = $condition->getConditionObject()->getText();
|
||||
|
||||
$condition->applyCustomData();
|
||||
|
||||
$this->setCacheConditionData($condition);
|
||||
|
||||
return $this->renderConditions($condition);
|
||||
}
|
||||
|
||||
public function onCreateCondition()
|
||||
{
|
||||
if (!$className = post('condition_class')) {
|
||||
throw new ValidationException(['condition_class' => 'Please specify a condition']);
|
||||
}
|
||||
|
||||
$this->restoreCacheConditionDataPayload();
|
||||
|
||||
$subcondition = null;
|
||||
|
||||
$parts = explode(':', $className);
|
||||
if (count($parts) > 1) {
|
||||
$subcondition = $parts[1];
|
||||
$className = $parts[0];
|
||||
}
|
||||
|
||||
$parentCondition = $this->findConditionObj();
|
||||
|
||||
$newCondition = $this->getRelationModel();
|
||||
$newCondition->class_name = $className;
|
||||
$newCondition->rule_host_type = $parentCondition->rule_host_type;
|
||||
|
||||
if ($subcondition) {
|
||||
$newCondition->subcondition = $subcondition;
|
||||
}
|
||||
|
||||
$newCondition->save();
|
||||
|
||||
$parentCondition->children()->add($newCondition, post('_session_key').'_'.$parentCondition->id);
|
||||
|
||||
$this->vars['newConditionId'] = $newCondition->id;
|
||||
|
||||
return $this->renderConditions($parentCondition);
|
||||
}
|
||||
|
||||
public function onDeleteCondition()
|
||||
{
|
||||
$parentCondition = null;
|
||||
|
||||
$condition = $this->findConditionObj();
|
||||
|
||||
if ($parentId = $this->getParentIdFromCondition($condition)) {
|
||||
$parentCondition = $this->findConditionObj($parentId);
|
||||
|
||||
if ($parentCondition) {
|
||||
$parentCondition->children()->remove($condition, post('_session_key').'_'.$parentCondition->id);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->renderConditions($parentCondition);
|
||||
}
|
||||
|
||||
public function onCancelConditionSettings()
|
||||
{
|
||||
$condition = $this->findConditionObj(post('new_condition_id'));
|
||||
|
||||
$condition->delete();
|
||||
|
||||
return $this->renderConditions();
|
||||
}
|
||||
|
||||
//
|
||||
// Postback deferring
|
||||
//
|
||||
|
||||
public function getCacheConditionAttributes($condition)
|
||||
{
|
||||
return array_get($this->getCacheConditionData($condition), 'attributes');
|
||||
}
|
||||
|
||||
public function getCacheConditionText($condition)
|
||||
{
|
||||
return array_get($this->getCacheConditionData($condition), 'text');
|
||||
}
|
||||
|
||||
public function getCacheConditionJoinText($condition)
|
||||
{
|
||||
return array_get($this->getCacheConditionData($condition), 'joinText');
|
||||
}
|
||||
|
||||
public function getCacheConditionData($condition, $default = null)
|
||||
{
|
||||
$cache = post('condition_data', []);
|
||||
|
||||
if (is_array($cache) && array_key_exists($condition->id, $cache)) {
|
||||
return json_decode($cache[$condition->id], true);
|
||||
}
|
||||
|
||||
if ($default === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->makeCacheConditionData($condition);
|
||||
}
|
||||
|
||||
public function makeCacheConditionData($condition)
|
||||
{
|
||||
$data = [
|
||||
'attributes' => $condition->config_data,
|
||||
'text' => $condition->getText()
|
||||
];
|
||||
|
||||
if ($condition->isCompound()) {
|
||||
$data['joinText'] = $condition->getJoinText();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function setCacheConditionData($condition)
|
||||
{
|
||||
$cache = post('condition_data', []);
|
||||
|
||||
$cache[$condition->id] = json_encode($this->makeCacheConditionData($condition));
|
||||
|
||||
Request::merge([
|
||||
'condition_data' => $cache
|
||||
]);
|
||||
}
|
||||
|
||||
public function restoreCacheConditionDataPayload()
|
||||
{
|
||||
Request::merge([
|
||||
'condition_data' => json_decode(post('current_condition_data', []), true)
|
||||
]);
|
||||
}
|
||||
|
||||
public function getCacheConditionDataPayload()
|
||||
{
|
||||
return post('condition_data');
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
public function getParentIdFromCondition($condition)
|
||||
{
|
||||
if ($parentId = post('current_parent_id')) {
|
||||
return $parentId;
|
||||
}
|
||||
|
||||
$parentIds = post('condition_parent_id', []);
|
||||
|
||||
if (isset($parentIds[$condition->id])) {
|
||||
return $parentIds[$condition->id];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the primary rule conditions container
|
||||
* @return array
|
||||
*/
|
||||
protected function renderConditions($currentCondition = null)
|
||||
{
|
||||
if ($currentCondition && $this->isRootCondition($currentCondition)) {
|
||||
$condition = $currentCondition;
|
||||
}
|
||||
else {
|
||||
$condition = $this->getConditionsRoot();
|
||||
}
|
||||
|
||||
return [
|
||||
'#'.$this->getId() => $this->makePartial('conditions', ['condition' => $condition])
|
||||
];
|
||||
}
|
||||
|
||||
protected function makeConditionFormWidget()
|
||||
{
|
||||
if ($this->conditionFormWidget !== null) {
|
||||
return $this->conditionFormWidget;
|
||||
}
|
||||
|
||||
if (!$model = $this->findConditionObj(null, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$config = $model->getFieldConfig();
|
||||
$config->model = $model;
|
||||
$config->alias = $this->alias . 'Form';
|
||||
$config->arrayName = 'Condition';
|
||||
|
||||
$widget = $this->makeWidget('Backend\Widgets\Form', $config);
|
||||
|
||||
/*
|
||||
* Set form values based on postback or cached attributes
|
||||
*/
|
||||
if (!$data = post('Condition')) {
|
||||
$data = $this->getCacheConditionAttributes($model);
|
||||
}
|
||||
|
||||
$widget->setFormValues($data);
|
||||
|
||||
/*
|
||||
* Allow conditions to register their own widgets
|
||||
*/
|
||||
$model->onPreRender($this->controller, $this);
|
||||
|
||||
return $this->conditionFormWidget = $widget;
|
||||
}
|
||||
|
||||
protected function findConditionObj($conditionId = null, $throw = true)
|
||||
{
|
||||
$conditionId = $conditionId ? $conditionId : post('current_condition_id');
|
||||
|
||||
$condition = null;
|
||||
|
||||
if (strlen($conditionId)) {
|
||||
$condition = $this->getRelationModel()->find($conditionId);
|
||||
}
|
||||
|
||||
if ($throw && !$condition) {
|
||||
throw new ApplicationException('Condition not found');
|
||||
}
|
||||
|
||||
return $condition;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
.action-set {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.action-set ol {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.action-set ol > li {
|
||||
-webkit-transition: width 1s;
|
||||
transition: width 1s;
|
||||
}
|
||||
.action-set ol > li > div {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
position: relative;
|
||||
}
|
||||
.action-set ol > li > div > a,
|
||||
.action-set ol > li > div div.content {
|
||||
color: #2b3e50;
|
||||
padding: 10px 45px 10px 91px;
|
||||
display: block;
|
||||
line-height: 150%;
|
||||
text-decoration: none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.action-set ol > li > div span.icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
left: 20px;
|
||||
top: 15px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
color: #999;
|
||||
}
|
||||
.action-set ol > li > div span.icon > i {
|
||||
line-height: 22px;
|
||||
font-weight: normal;
|
||||
font-size: 22px;
|
||||
color: #999;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover,
|
||||
.action-set ol > li > div.popover-highlight {
|
||||
background-color: #4ea5e0 !important;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover span.icon > i,
|
||||
.action-set ol > li > div.popover-highlight span.icon > i {
|
||||
color: #fff !important;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover > a,
|
||||
.action-set ol > li > div.popover-highlight > a {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover:before,
|
||||
.action-set ol > li > div.popover-highlight:before {
|
||||
background-position: 0px -80px;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover:after,
|
||||
.action-set ol > li > div.popover-highlight:after {
|
||||
top: 0 !important;
|
||||
bottom: 0 !important;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover span,
|
||||
.action-set ol > li > div.popover-highlight span {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):hover span.drag-handle,
|
||||
.action-set ol > li > div.popover-highlight span.drag-handle {
|
||||
cursor: move;
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):active {
|
||||
background-color: #3498db !important;
|
||||
}
|
||||
.action-set ol > li > div:not(.no-hover):active > a {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.action-set ol > li > div span.icon:first-child {
|
||||
font-size: 11px;
|
||||
color: #ccc;
|
||||
}
|
||||
.action-set ol > li > div span.icon:first-child > i {
|
||||
color: #ccc;
|
||||
font-size: 15px;
|
||||
}
|
||||
.action-set ol > li > div span.icon:last-child {
|
||||
left: 52px;
|
||||
}
|
||||
.action-set ol > li > div span.comment {
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
color: #95a5a6;
|
||||
font-size: 13px;
|
||||
margin-top: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.action-set ol > li > div > .subpanel {
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 200;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.action-set ol > li > div > ul.submenu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
z-index: 200;
|
||||
height: 100%;
|
||||
display: none;
|
||||
margin: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
.action-set ol > li > div > ul.submenu li {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
background: #2581b8;
|
||||
border-right: 1px solid #328ec8;
|
||||
}
|
||||
.action-set ol > li > div > ul.submenu li p {
|
||||
display: table;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.action-set ol > li > div > ul.submenu li p a {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
font-size: 13px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.action-set ol > li > div > ul.submenu li p a i.control-icon {
|
||||
font-size: 22px;
|
||||
}
|
||||
.action-set ol > li > div:hover > ul.submenu {
|
||||
display: block;
|
||||
}
|
||||
.action-set ol > li > div .checkbox {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 0;
|
||||
}
|
||||
.action-set ol > li > div .checkbox label {
|
||||
margin-right: 0;
|
||||
}
|
||||
.action-set ol > li > div .checkbox label:before {
|
||||
border-color: #cccccc;
|
||||
}
|
||||
.action-set ol > li > div.popover-highlight {
|
||||
background-color: #4ea5e0 !important;
|
||||
}
|
||||
.action-set ol > li > div.popover-highlight:before {
|
||||
background-position: 0px -80px;
|
||||
}
|
||||
.action-set ol > li > div.popover-highlight > a {
|
||||
color: #ffffff !important;
|
||||
cursor: default;
|
||||
}
|
||||
.action-set ol > li > div.popover-highlight span {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.action-set ol > li > div.popover-highlight > ul.submenu,
|
||||
.action-set ol > li > div.popover-highlight > span.drag-handle {
|
||||
display: none !important;
|
||||
}
|
||||
.action-set ol > li.active > div {
|
||||
background: #dddddd;
|
||||
}
|
||||
.action-set ol > li.active > div:after {
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
left: 0;
|
||||
top: -1px;
|
||||
bottom: -1px;
|
||||
background: #e67e22;
|
||||
display: block;
|
||||
content: ' ';
|
||||
}
|
||||
.action-set ol > li.active > div > span.comment,
|
||||
.action-set ol > li.active > div > span.expand {
|
||||
color: #8f8f8f;
|
||||
}
|
||||
.action-set a.menu-control {
|
||||
display: block;
|
||||
margin: 20px;
|
||||
padding: 13px 15px;
|
||||
border: dotted 2px #ebebeb;
|
||||
color: #bdc3c7;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
border-radius: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.action-set a.menu-control:hover,
|
||||
.action-set a.menu-control:focus {
|
||||
text-decoration: none;
|
||||
background-color: #4ea5e0;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
padding: 15px 17px;
|
||||
}
|
||||
.action-set a.menu-control:active {
|
||||
background: #3498db;
|
||||
color: #ffffff;
|
||||
}
|
||||
.action-set a.menu-control i {
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Global helpers
|
||||
*/
|
||||
function showActionSettings(id) {
|
||||
var $control = $('[data-action-id='+id+']').closest('[data-control="ruleactions"]')
|
||||
|
||||
$control.ruleActions('onShowNewActionSettings', id)
|
||||
}
|
||||
|
||||
/*
|
||||
* Plugin definition
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
var Base = $.oc.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var RuleActions = function (element, options) {
|
||||
this.$el = $(element)
|
||||
this.options = options || {}
|
||||
|
||||
$.oc.foundation.controlUtils.markDisposable(element)
|
||||
Base.call(this)
|
||||
this.init()
|
||||
}
|
||||
|
||||
RuleActions.prototype = Object.create(BaseProto)
|
||||
RuleActions.prototype.constructor = RuleActions
|
||||
|
||||
RuleActions.prototype.init = function() {
|
||||
this.$el.on('click', '[data-actions-settings]', this.proxy(this.onShowSettings))
|
||||
this.$el.on('click', '[data-actions-delete]', this.proxy(this.onDeleteAction))
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
}
|
||||
|
||||
RuleActions.prototype.dispose = function() {
|
||||
this.$el.off('click', '[data-actions-settings]', this.proxy(this.onShowSettings))
|
||||
this.$el.off('click', '[data-actions-delete]', this.proxy(this.onDeleteAction))
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.ruleActions')
|
||||
|
||||
this.$el = null
|
||||
|
||||
// In some cases options could contain callbacks,
|
||||
// so it's better to clean them up too.
|
||||
this.options = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
RuleActions.prototype.onDeleteAction = function(event) {
|
||||
var $el = $(event.target),
|
||||
actionId = getActionIdFromElement($el)
|
||||
|
||||
$el.request(this.options.deleteHandler, {
|
||||
data: { current_action_id: actionId },
|
||||
confirm: 'Do you really want to delete this action?'
|
||||
})
|
||||
}
|
||||
|
||||
RuleActions.prototype.onShowNewActionSettings = function(actionId) {
|
||||
var $el = $('[data-action-id='+actionId+']')
|
||||
|
||||
var popup_size = 'giant';
|
||||
// Action does not use settings
|
||||
if ($el.hasClass('no-form')) {
|
||||
// Popup will contain scheduling option only
|
||||
popup_size = 'medium'
|
||||
}
|
||||
|
||||
$el.popup({
|
||||
handler: this.options.settingsHandler,
|
||||
extraData: { current_action_id: actionId },
|
||||
size: popup_size
|
||||
})
|
||||
|
||||
// This will not fire on successful save because the target element
|
||||
// is replaced by the time the popup loader has finished to call it
|
||||
$el.one('hide.oc.popup', this.proxy(this.onCancelAction))
|
||||
}
|
||||
|
||||
RuleActions.prototype.onCancelAction = function(event) {
|
||||
var $el = $(event.target),
|
||||
actionId = getActionIdFromElement($el)
|
||||
|
||||
$el.request(this.options.cancelHandler, {
|
||||
data: { new_action_id: actionId }
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
RuleActions.prototype.onShowSettings = function(event) {
|
||||
var $el = $(event.target),
|
||||
actionId = getActionIdFromElement($el)
|
||||
|
||||
var popup_size = 'giant';
|
||||
// Action does not use settings
|
||||
if ($el.closest('li.action-item').hasClass('no-form')) {
|
||||
popup_size = 'medium'
|
||||
}
|
||||
|
||||
$el.popup({
|
||||
handler: this.options.settingsHandler,
|
||||
extraData: { current_action_id: actionId },
|
||||
size: popup_size
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function getActionIdFromElement($el) {
|
||||
var $item = $el.closest('li.action-item')
|
||||
|
||||
return $item.data('action-id')
|
||||
}
|
||||
|
||||
RuleActions.DEFAULTS = {
|
||||
settingsHandler: null,
|
||||
deleteHandler: null,
|
||||
cancelHandler: null,
|
||||
createHandler: null
|
||||
}
|
||||
|
||||
// PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.ruleActions
|
||||
|
||||
$.fn.ruleActions = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), items, result
|
||||
|
||||
items = this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.ruleActions')
|
||||
var options = $.extend({}, RuleActions.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.ruleActions', (data = new RuleActions(this, options)))
|
||||
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : items
|
||||
}
|
||||
|
||||
$.fn.ruleActions.Constructor = RuleActions
|
||||
|
||||
$.fn.ruleActions.noConflict = function () {
|
||||
$.fn.ruleActions = old
|
||||
return this
|
||||
}
|
||||
|
||||
// Add this only if required
|
||||
$(document).render(function (){
|
||||
$('[data-control="ruleactions"]').ruleActions()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
@import "../../../../../../../modules/backend/assets/less/core/boot.less";
|
||||
|
||||
//
|
||||
// Actions
|
||||
// --------------------------------------------------
|
||||
|
||||
@color-actions-item-bg: #ffffff;
|
||||
@color-actions-item-title: #2b3e50;
|
||||
@color-actions-item-comment: #95a5a6;
|
||||
@color-actions-control: #bdc3c7;
|
||||
@color-actions-hover-bg: @highlight-hover-bg;
|
||||
@color-actions-hover-text: @highlight-hover-text;
|
||||
@color-actions-active-bg: @highlight-active-bg;
|
||||
@color-actions-active-text: @highlight-active-text;
|
||||
@color-actions-item-active-comment: #8f8f8f;
|
||||
@color-actions-submenu-text: #ffffff;
|
||||
@color-actions-light-submenu-bg: #2581b8;
|
||||
@color-actions-light-submenu-border: #328ec8;
|
||||
|
||||
.action-set {
|
||||
margin-top: 10px;
|
||||
|
||||
ol {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
> li {
|
||||
.transition(width 1s);
|
||||
|
||||
> div {
|
||||
font-size: @font-size-base;
|
||||
font-weight: normal;
|
||||
position: relative;
|
||||
|
||||
> a, div.content {
|
||||
color: @color-actions-item-title;
|
||||
padding: 10px 45px 10px 91px;
|
||||
display: block;
|
||||
line-height: 150%;
|
||||
text-decoration: none;
|
||||
.box-sizing(border-box);
|
||||
}
|
||||
|
||||
span.icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
left: 20px;
|
||||
top: 15px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
color: #999;
|
||||
|
||||
> i {
|
||||
line-height: 22px;
|
||||
font-weight: normal;
|
||||
font-size: 22px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.no-hover):hover, &.popover-highlight {
|
||||
background-color: @color-actions-hover-bg !important;
|
||||
|
||||
span.icon > i {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
> a {
|
||||
color: @color-actions-hover-text !important;
|
||||
}
|
||||
|
||||
&:before {
|
||||
background-position: 0px -80px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
top: 0 !important;
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
span {
|
||||
color: @color-actions-hover-text !important;
|
||||
|
||||
&.drag-handle {
|
||||
cursor: move;
|
||||
.opacity(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.no-hover):active {
|
||||
background-color: @color-actions-active-bg !important;
|
||||
|
||||
> a {
|
||||
color: @color-actions-active-text !important;
|
||||
}
|
||||
}
|
||||
|
||||
span.icon:first-child {
|
||||
font-size: 11px;
|
||||
color: #ccc;
|
||||
|
||||
> i {
|
||||
color: #ccc;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
span.icon:last-child {
|
||||
left: 52px;
|
||||
}
|
||||
|
||||
span.comment {
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
color: @color-actions-item-comment;
|
||||
font-size: @font-size-base - 1;
|
||||
margin-top: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
> .subpanel {
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 200;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
> ul.submenu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
z-index: 200;
|
||||
height: 100%;
|
||||
display: none;
|
||||
margin: 0;
|
||||
font-size: 0;
|
||||
|
||||
li {
|
||||
font-size: @font-size-base - 2;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
background: @color-actions-light-submenu-bg;
|
||||
border-right: 1px solid @color-actions-light-submenu-border;
|
||||
|
||||
p {
|
||||
display: table;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
a {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
font-size: @font-size-base - 1;
|
||||
.box-sizing(border-box);
|
||||
color: @color-actions-submenu-text;
|
||||
text-decoration: none;
|
||||
|
||||
i.control-icon {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> ul.submenu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 0;
|
||||
|
||||
label {
|
||||
margin-right: 0;
|
||||
|
||||
&:before {
|
||||
border-color: @color-filelist-cb-border;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.popover-highlight {
|
||||
background-color: @color-actions-hover-bg !important;
|
||||
|
||||
&:before {
|
||||
background-position: 0px -80px;
|
||||
}
|
||||
|
||||
> a {
|
||||
color: @color-actions-hover-text !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
span {
|
||||
color: @color-actions-hover-text !important;
|
||||
}
|
||||
|
||||
> ul.submenu, > span.drag-handle {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
> div {
|
||||
background: @color-list-active;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
left: 0;
|
||||
top: -1px;
|
||||
bottom: -1px;
|
||||
background: @color-list-active-border;
|
||||
display: block;
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
> span.comment, > span.expand {
|
||||
color: @color-actions-item-active-comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.menu-control {
|
||||
display: block;
|
||||
margin: 20px;
|
||||
padding: 13px 15px;
|
||||
border: dotted 2px #ebebeb;
|
||||
color: #bdc3c7;
|
||||
font-size: @font-size-base - 2;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
border-radius: 5px;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover, &:focus {
|
||||
text-decoration: none;
|
||||
background-color: @color-actions-hover-bg;
|
||||
color: @color-actions-hover-text;
|
||||
border: none;
|
||||
padding: 15px 17px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: @color-actions-active-bg;
|
||||
color: @color-actions-active-text;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<li
|
||||
id="<?= 'action'.$action->id ?>"
|
||||
data-action-id="<?= $action->id ?>"
|
||||
class="action-item <?= $action->hasFieldConfig() ? '' : 'no-form' ?>">
|
||||
<div>
|
||||
<div class="icons">
|
||||
<span class="icon"><i class="icon-bolt"></i></span>
|
||||
<span class="icon"><i class="<?= e($action->getActionIcon()) ?>"></i></span>
|
||||
</div>
|
||||
<a href="javascript:;" data-actions-settings>
|
||||
<span class="title"><?= e(__($this->getCacheActionTitle($action))) ?></span>
|
||||
<span class="comment"><?= e(__($this->getCacheActionText($action))) ?></span>
|
||||
</a>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<p>
|
||||
<a href="javascript:;" data-actions-delete>
|
||||
<i class="icon-trash-o control-icon"></i>
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="action_data[<?= $action->id ?>]"
|
||||
value="<?= e(json_encode($this->getCacheActionData($action))) ?>" />
|
||||
</li>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?= Form::open(['id' => 'propertyForm']) ?>
|
||||
|
||||
<div class="modal-header flex-row-reverse">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title">
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<?= e(__($action->getTitle())) ?>
|
||||
<?php else: ?>
|
||||
<?= e(__('Action')) ?>
|
||||
<?php endif ?>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<input type="hidden" name="current_action_id" value="<?= $action->id ?>" />
|
||||
<input type="hidden" name="current_action_data" value="<?= e(json_encode($this->getCacheActionDataPayload())) ?>" />
|
||||
|
||||
<div class="modal-body">
|
||||
<?php if ($actionFormWidget): ?>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<?= $actionFormWidget->render() ?>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<?= $this->makePartial('available_tags') ?>
|
||||
|
||||
<?= $this->makePartial('schedule') ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?= $this->makePartial('schedule') ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
data-request="<?= $this->getEventHandler('onSaveAction') ?>"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-popup-load-indicator
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="oc-icon-trash-o btn-icon danger pull-right"
|
||||
data-request="<?= $this->getEventHandler('onDeleteAction') ?>"
|
||||
data-popup-load-indicator
|
||||
data-request-confirm="<?= e(__('Do you really want to delete this action?')) ?>">
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php Form::close() ?>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<div class="action-set">
|
||||
<ol>
|
||||
<li>
|
||||
<div class="no-hover">
|
||||
<div class="icons">
|
||||
<span class="icon"><?= e(__('ON')) ?></span>
|
||||
<span class="icon"><i class="icon-user"></i></span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span class="title"><?= e($formModel->getEventName()) ?></span>
|
||||
<span class="comment"><?= e($formModel->getEventDescription()) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<?php foreach ($actions as $action): ?>
|
||||
<?= $this->makePartial('action', ['action' => $action]) ?>
|
||||
<?php endforeach ?>
|
||||
</ol>
|
||||
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-control="popup"
|
||||
data-handler="<?= $this->getEventHandler('onLoadCreateActionForm') ?>"
|
||||
class="menu-control">
|
||||
<i class="icon-plus"></i> <?= e(__('Add Action')) ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (isset($newActionId)): ?>
|
||||
<script> showActionSettings('<?= e($newActionId) ?>') </script>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<div class="actions-container">
|
||||
<div
|
||||
id="<?= $this->getId() ?>"
|
||||
class="control-ruleactions"
|
||||
data-settings-handler="<?= $this->getEventHandler('onLoadActionSetup') ?>"
|
||||
data-delete-handler="<?= $this->getEventHandler('onDeleteAction') ?>"
|
||||
data-cancel-handler="<?= $this->getEventHandler('onCancelActionSettings') ?>"
|
||||
data-create-handler="<?= $this->getEventHandler('onLoadCreateChildAction') ?>"
|
||||
data-control="ruleactions">
|
||||
<?= $this->makePartial('actions', ['actions' => $actions]) ?>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="<?= $name ?>" value="" />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php if ($availableTags): ?>
|
||||
<div class="form-preview">
|
||||
<p><?= e(__('These variables are available')) ?></p>
|
||||
<div class="control-balloon-selector">
|
||||
<ul>
|
||||
<?php foreach ($availableTags as $tag => $description): ?>
|
||||
<li
|
||||
title="<?= $description ?>"
|
||||
data-control="dragvalue"
|
||||
data-drag-click="true"
|
||||
data-text-value="{{ <?= $tag ?> }}">
|
||||
{{ <?= $tag ?> }}
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="small"><em><?= e(__('Click or drag these in to the content area')) ?></em></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="form-preview">
|
||||
<div><?= e(__('This action does not provide any variables.')) ?></div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?= Form::open(['id' => 'addRuleActionForm']) ?>
|
||||
|
||||
<input type="hidden" name="current_action_data" value="<?= e(json_encode($this->getCacheActionDataPayload())) ?>" />
|
||||
|
||||
<div class="modal-header flex-row-reverse">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(__('Add Notification Action')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<?php if ($this->fatalError): ?>
|
||||
<p class="flash-message static error"><?= $fatalError ?></p>
|
||||
<?php else: ?>
|
||||
|
||||
<div class="control-simplelist is-selectable is-scrollable size-large" data-control="simplelist">
|
||||
<ul>
|
||||
<?php foreach ($actions as $class => $action): ?>
|
||||
|
||||
<li>
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-request="<?= $this->getEventHandler('onCreateAction') ?>"
|
||||
data-request-data="action_class: '<?= addslashes($class) ?>'"
|
||||
data-popup-load-indicator>
|
||||
<h5 class="heading"><?= e(trans($action->getActionName())) ?></h5>
|
||||
<p class="description"><?= e(trans($action->getActionDescription())) ?></p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<div class="form-preview">
|
||||
<p>
|
||||
<i class="icon-clock-o"></i>
|
||||
<?= e(__('Schedule')) ?>
|
||||
</p>
|
||||
<p>
|
||||
<?= $actionScheduleFormWidget->render() ?>
|
||||
</p>
|
||||
<div class="small">
|
||||
<em>
|
||||
<?php if (in_array($queueDriver, ['sync', 'sqs'])): ?>
|
||||
<i class="icon-warning"></i>
|
||||
<?= e(__('The configured queue driver does not support delayed execution.')) ?>
|
||||
<?php else: ?>
|
||||
<?= e(__('Note that scheduling might not work for certain queue driver configurations.')) ?>
|
||||
<?php endif ?>
|
||||
</em>
|
||||
<a target="_blank" href="https://octobercms.com/docs/services/queues#basic-usage">
|
||||
<?= e(__('Learn more')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
ul.condition-set {
|
||||
padding: 5px 0 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
ul.condition-set ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
ul.condition-set li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul.condition-set .condition-item {
|
||||
border-radius: 4px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-root {
|
||||
border-radius: 0;
|
||||
}
|
||||
ul.condition-set .condition-item.collapsed .compound-content {
|
||||
border-left-color: transparent !important;
|
||||
}
|
||||
ul.condition-set .condition-item.collapsed .condition-set {
|
||||
display: none;
|
||||
}
|
||||
ul.condition-set .condition-item.collapsed.is-inner .compound-add-item {
|
||||
display: none;
|
||||
}
|
||||
ul.condition-set .condition-item .condition-delete {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
right: 7px;
|
||||
top: 7px;
|
||||
}
|
||||
ul.condition-set a.condition-collapse {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 2px;
|
||||
}
|
||||
ul.condition-set a.condition-collapse > i {
|
||||
-webkit-transition: transform 0.3s;
|
||||
transition: transform 0.3s;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
color: #bdc3c7;
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
}
|
||||
ul.condition-set a.condition-collapse:hover > i {
|
||||
color: #999;
|
||||
}
|
||||
ul.condition-set li.collapsed a.condition-collapse > i {
|
||||
-webkit-transform: scale(1, -1);
|
||||
-ms-transform: scale(1, -1);
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
ul.condition-set .compound-join {
|
||||
text-align: center;
|
||||
padding: 3px 0 2px 0;
|
||||
}
|
||||
ul.condition-set .compound-join a {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
}
|
||||
ul.condition-set .compound-join a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single {
|
||||
padding: 8px 20px 9px 24px;
|
||||
background: #fff;
|
||||
border: 1px solid #eee;
|
||||
position: relative;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single:before {
|
||||
font-family: FontAwesome;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
*margin-right: .3em;
|
||||
content: "\f126";
|
||||
line-height: 100%;
|
||||
font-size: 16px;
|
||||
color: #bdc3c7;
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
left: 12px;
|
||||
top: 11px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single .condition-text {
|
||||
padding-left: 10px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single .condition-text a {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single .condition-text a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single .condition-text a span.operator {
|
||||
font-weight: 500;
|
||||
}
|
||||
ul.condition-set .condition-item.is-single a.condition-delete {
|
||||
right: 12px;
|
||||
top: 9px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-content {
|
||||
position: relative;
|
||||
border-left: 1px solid #dbdee0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-content:before {
|
||||
color: #bdc3c7;
|
||||
font-family: FontAwesome;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
*margin-right: .3em;
|
||||
content: "\f111";
|
||||
font-size: 8px;
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: -2px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-content h4 {
|
||||
padding: 0 30px 5px 5px;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
font-size: 13px;
|
||||
line-height: 130%;
|
||||
font-weight: 400;
|
||||
color: #666;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-content h4 a.condition-text {
|
||||
color: #333333;
|
||||
padding-left: 10px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-content h4 a.condition-text:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-content .condition-set {
|
||||
padding-left: 15px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-left: 20px;
|
||||
border: 2px dotted #e0e0e0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:before {
|
||||
color: #bdc3c7;
|
||||
font-family: FontAwesome;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
*margin-right: .3em;
|
||||
content: "\f067";
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
left: -23px;
|
||||
top: -11px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item > a {
|
||||
color: #bdc3c7;
|
||||
text-align: center;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 13px 15px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:hover,
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:focus {
|
||||
background-color: #4ea5e0;
|
||||
border-color: #4ea5e0;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:hover:before,
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:focus:before {
|
||||
color: #999;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:hover > a,
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:focus > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:active {
|
||||
background: #3498db;
|
||||
border-color: #3498db;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound .compound-add-item:active > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound.is-inner {
|
||||
padding-right: 20px;
|
||||
}
|
||||
ul.condition-set .condition-item.is-compound.is-inner a.condition-delete-compound {
|
||||
right: -7px;
|
||||
top: 0;
|
||||
}
|
||||
.condition-multi-value .condition-filter-search {
|
||||
background-position: right -81px !important;
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
border-radius: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.condition-multi-value .filter-list {
|
||||
margin: 0 -20px;
|
||||
}
|
||||
.condition-multi-value .filter-list .list-footer {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.condition-multi-value .added-filter-list {
|
||||
margin: 0 -20px;
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Global helpers
|
||||
*/
|
||||
function showConditionSettings(id) {
|
||||
var $control = $('[data-condition-id='+id+']').closest('[data-control="ruleconditions"]')
|
||||
|
||||
$control.ruleConditions('onShowNewConditionSettings', id)
|
||||
}
|
||||
|
||||
/*
|
||||
* Plugin definition
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
var Base = $.oc.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var RuleConditions = function (element, options) {
|
||||
this.$el = $(element)
|
||||
this.options = options || {}
|
||||
|
||||
$.oc.foundation.controlUtils.markDisposable(element)
|
||||
Base.call(this)
|
||||
this.init()
|
||||
}
|
||||
|
||||
RuleConditions.prototype = Object.create(BaseProto)
|
||||
RuleConditions.prototype.constructor = RuleConditions
|
||||
|
||||
RuleConditions.prototype.init = function() {
|
||||
this.$el.on('click', '[data-conditions-collapse]', this.proxy(this.onConditionsToggle))
|
||||
this.$el.on('click', '[data-conditions-settings]', this.proxy(this.onShowSettings))
|
||||
this.$el.on('click', '[data-conditions-create]', this.proxy(this.onCreateChildCondition))
|
||||
this.$el.on('click', '[data-conditions-delete]', this.proxy(this.onDeleteCondition))
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
}
|
||||
|
||||
RuleConditions.prototype.dispose = function() {
|
||||
this.$el.off('click', '[data-conditions-collapse]', this.proxy(this.onConditionsToggle))
|
||||
this.$el.off('click', '[data-conditions-settings]', this.proxy(this.onShowSettings))
|
||||
this.$el.off('click', '[data-conditions-create]', this.proxy(this.onCreateChildCondition))
|
||||
this.$el.off('click', '[data-conditions-delete]', this.proxy(this.onDeleteCondition))
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.ruleConditions')
|
||||
|
||||
this.$el = null
|
||||
|
||||
// In some cases options could contain callbacks,
|
||||
// so it's better to clean them up too.
|
||||
this.options = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
RuleConditions.prototype.onDeleteCondition = function(event) {
|
||||
var $el = $(event.target),
|
||||
conditionId = getConditionIdFromElement($el)
|
||||
|
||||
$el.request(this.options.deleteHandler, {
|
||||
data: { current_condition_id: conditionId },
|
||||
confirm: 'Do you really want to delete this condition?'
|
||||
})
|
||||
}
|
||||
|
||||
RuleConditions.prototype.onCreateChildCondition = function(event) {
|
||||
var $el = $(event.target),
|
||||
conditionId = getConditionIdFromElement($el)
|
||||
|
||||
$el.popup({
|
||||
handler: this.options.createHandler,
|
||||
extraData: { current_condition_id: conditionId },
|
||||
size: 'large'
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
RuleConditions.prototype.onConditionsToggle = function(event) {
|
||||
var $el = $(event.target),
|
||||
$item = $el.closest('li'),
|
||||
newStatusValue = $item.hasClass('collapsed') ? 0 : 1,
|
||||
conditionId = getConditionIdFromElement($el)
|
||||
|
||||
$el.request(this.options.collapseHandler, {
|
||||
data: {
|
||||
status: newStatusValue,
|
||||
group: conditionId
|
||||
}
|
||||
})
|
||||
|
||||
if (newStatusValue) {
|
||||
$el.parents('li:first').addClass('collapsed');
|
||||
}
|
||||
else {
|
||||
$el.parents('li:first').removeClass('collapsed');
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
RuleConditions.prototype.onShowNewConditionSettings = function(conditionId) {
|
||||
var $el = $('[data-condition-id='+conditionId+']')
|
||||
|
||||
$el.popup({
|
||||
handler: this.options.settingsHandler,
|
||||
extraData: { current_condition_id: conditionId },
|
||||
size: 'large'
|
||||
})
|
||||
|
||||
// This will not fire on successful save because the target element
|
||||
// is replaced by the time the popup loader has finished to call it
|
||||
$el.one('hide.oc.popup', this.proxy(this.onCancelCondition))
|
||||
}
|
||||
|
||||
RuleConditions.prototype.onCancelCondition = function(event) {
|
||||
var $el = $(event.target),
|
||||
conditionId = getConditionIdFromElement($el)
|
||||
|
||||
$el.request(this.options.cancelHandler, {
|
||||
data: { new_condition_id: conditionId }
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
RuleConditions.prototype.onShowSettings = function(event) {
|
||||
var $el = $(event.target),
|
||||
conditionId = getConditionIdFromElement($el)
|
||||
|
||||
$el.popup({
|
||||
handler: this.options.settingsHandler,
|
||||
extraData: { current_condition_id: conditionId },
|
||||
size: 'large'
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function getConditionIdFromElement($el) {
|
||||
var $item = $el.closest('li.condition-item')
|
||||
|
||||
return $item.data('condition-id')
|
||||
}
|
||||
|
||||
RuleConditions.DEFAULTS = {
|
||||
collapseHandler: null,
|
||||
settingsHandler: null,
|
||||
deleteHandler: null,
|
||||
cancelHandler: null,
|
||||
createHandler: null
|
||||
}
|
||||
|
||||
// PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.ruleConditions
|
||||
|
||||
$.fn.ruleConditions = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), items, result
|
||||
|
||||
items = this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.ruleConditions')
|
||||
var options = $.extend({}, RuleConditions.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.ruleConditions', (data = new RuleConditions(this, options)))
|
||||
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : items
|
||||
}
|
||||
|
||||
$.fn.ruleConditions.Constructor = RuleConditions
|
||||
|
||||
$.fn.ruleConditions.noConflict = function () {
|
||||
$.fn.ruleConditions = old
|
||||
return this
|
||||
}
|
||||
|
||||
// Add this only if required
|
||||
$(document).render(function (){
|
||||
$('[data-control="ruleconditions"]').ruleConditions()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Plugin definition
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
var Base = $.oc.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var ConditionMultiValue = function (element, options) {
|
||||
this.$el = $(element)
|
||||
this.options = options || {}
|
||||
this.$template = $('[data-record-template]', this.$el)
|
||||
this.$emptyTemplate = $('[data-empty-record-template]', this.$el)
|
||||
this.$addedRecords = $('[data-added-records]', this.$el)
|
||||
|
||||
$.oc.foundation.controlUtils.markDisposable(element)
|
||||
Base.call(this)
|
||||
|
||||
this.init()
|
||||
this.recompileData()
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype = Object.create(BaseProto)
|
||||
ConditionMultiValue.prototype.constructor = ConditionMultiValue
|
||||
|
||||
ConditionMultiValue.prototype.init = function() {
|
||||
this.$el.on('click', '[data-multivalue-add-record]', this.proxy(this.onAddRecord))
|
||||
this.$el.on('click', '[data-multivalue-remove-record]', this.proxy(this.onRemoveRecord))
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype.dispose = function() {
|
||||
this.$el.off('click', '[data-multivalue-add-record]', this.proxy(this.onAddRecord))
|
||||
this.$el.off('click', '[data-multivalue-remove-record]', this.proxy(this.onRemoveRecord))
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.conditionMultiValue')
|
||||
|
||||
this.$el = null
|
||||
|
||||
// In some cases options could contain callbacks,
|
||||
// so it's better to clean them up too.
|
||||
this.options = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype.onAddRecord = function(event) {
|
||||
var $el = $(event.target),
|
||||
recordId = $el.closest('[data-record-key]').data('record-key'),
|
||||
recordValue = $el.closest('a').data('record-value')
|
||||
|
||||
if (!!$('[data-record-key='+recordId+']', this.$addedRecords).length) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$addedRecords.append(this.renderTemplate({
|
||||
key: recordId,
|
||||
value: recordValue
|
||||
}))
|
||||
|
||||
this.recompileData()
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype.onRemoveRecord = function(event) {
|
||||
var $el = $(event.target),
|
||||
recordId = $el.closest('[data-record-key]').data('record-key')
|
||||
|
||||
$('[data-record-key='+recordId+']', this.$addedRecords).remove()
|
||||
|
||||
this.recompileData()
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype.recompileData = function(params) {
|
||||
var $recordElements = $('[data-record-key]', this.$addedRecords),
|
||||
hasData = !!$recordElements.length
|
||||
|
||||
if (hasData) {
|
||||
$('[data-no-record-data]', this.$addedRecords).remove()
|
||||
}
|
||||
else {
|
||||
this.$addedRecords.append(this.renderEmptyTemplate())
|
||||
}
|
||||
|
||||
if (this.options.dataLocker) {
|
||||
var $locker = $(this.options.dataLocker),
|
||||
selectedIds = []
|
||||
|
||||
$recordElements.each(function(key, record) {
|
||||
selectedIds.push($(record).data('record-key'))
|
||||
})
|
||||
|
||||
$locker.val(selectedIds.join(','))
|
||||
}
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype.renderTemplate = function(params) {
|
||||
return Mustache.render(this.$template.html(), params)
|
||||
}
|
||||
|
||||
ConditionMultiValue.prototype.renderEmptyTemplate = function() {
|
||||
return this.$emptyTemplate.html()
|
||||
}
|
||||
|
||||
ConditionMultiValue.DEFAULTS = {
|
||||
dataLocker: null
|
||||
}
|
||||
|
||||
// PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.conditionMultiValue
|
||||
|
||||
$.fn.conditionMultiValue = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), items, result
|
||||
|
||||
items = this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.conditionMultiValue')
|
||||
var options = $.extend({}, ConditionMultiValue.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.conditionMultiValue', (data = new ConditionMultiValue(this, options)))
|
||||
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : items
|
||||
}
|
||||
|
||||
$.fn.conditionMultiValue.Constructor = ConditionMultiValue
|
||||
|
||||
$.fn.conditionMultiValue.noConflict = function () {
|
||||
$.fn.conditionMultiValue = old
|
||||
return this
|
||||
}
|
||||
|
||||
// Add this only if required
|
||||
$(document).render(function (){
|
||||
$('[data-control="condition-multivalue"]').conditionMultiValue()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
@import "../../../../../../../modules/backend/assets/less/core/boot.less";
|
||||
|
||||
//
|
||||
// Conditions
|
||||
// --------------------------------------------------
|
||||
|
||||
ul.condition-set {
|
||||
padding: 5px 0 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.condition-item {
|
||||
border-radius: 4px;
|
||||
|
||||
&.is-root {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&.is-inner {}
|
||||
|
||||
&.collapsed {
|
||||
.compound-content {
|
||||
border-left-color: transparent !important;
|
||||
}
|
||||
|
||||
.condition-set {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-inner .compound-add-item {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-delete {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
right: 7px;
|
||||
top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Collapse
|
||||
//
|
||||
|
||||
a.condition-collapse {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 2px;
|
||||
|
||||
> i {
|
||||
.transition(~'transform 0.3s');
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
color: #bdc3c7;
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> i {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.collapsed a.condition-collapse {
|
||||
> i {
|
||||
.transform(scale(1,-1));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Joins
|
||||
//
|
||||
|
||||
.compound-join {
|
||||
text-align: center;
|
||||
padding: 3px 0 2px 0;
|
||||
a {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Single
|
||||
//
|
||||
|
||||
.condition-item.is-single {
|
||||
padding: 8px 20px 9px 24px;
|
||||
background: #fff;
|
||||
border: 1px solid #eee;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
.icon(@code-fork);
|
||||
line-height: 100%;
|
||||
font-size: 16px;
|
||||
color: #bdc3c7;
|
||||
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
left: 12px;
|
||||
top: 11px;
|
||||
}
|
||||
|
||||
.condition-text {
|
||||
padding-left: 10px;
|
||||
|
||||
a {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
span.operator {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.condition-delete {
|
||||
right: 12px;
|
||||
top: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Compound
|
||||
//
|
||||
|
||||
.condition-item.is-compound {
|
||||
.compound-content {
|
||||
position: relative;
|
||||
border-left: 1px solid #dbdee0;
|
||||
margin-left: 5px;
|
||||
|
||||
&:before {
|
||||
color: #bdc3c7;
|
||||
.icon(@circle);
|
||||
font-size: 8px;
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding: 0 30px 5px 5px;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
font-size: 13px;
|
||||
line-height: 130%;
|
||||
font-weight: 400;
|
||||
color: #666;
|
||||
a.condition-text {
|
||||
color: #333333;
|
||||
padding-left: 10px;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.condition-set {
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-item.is-compound {
|
||||
|
||||
}
|
||||
|
||||
.compound-add-item {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-left: 20px;
|
||||
border: 2px dotted #e0e0e0;
|
||||
border-radius: 5px;
|
||||
|
||||
&:before {
|
||||
color: #bdc3c7;
|
||||
.icon(@plus);
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
left: -23px;
|
||||
top: -11px;
|
||||
}
|
||||
|
||||
> a {
|
||||
color: #bdc3c7;
|
||||
text-align: center;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 13px 15px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: @font-size-base - 2;
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: @highlight-hover-bg;
|
||||
border-color: @highlight-hover-bg;
|
||||
|
||||
&:before {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
> a {
|
||||
color: @highlight-hover-text;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: @highlight-active-bg;
|
||||
border-color: @highlight-active-bg;
|
||||
> a {
|
||||
color: @highlight-active-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Compound (Inner)
|
||||
//
|
||||
|
||||
.condition-item.is-compound.is-inner {
|
||||
padding-right: 20px;
|
||||
|
||||
a.condition-delete-compound {
|
||||
right: -7px;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Multi value
|
||||
//
|
||||
|
||||
.condition-multi-value {
|
||||
.condition-filter-search {
|
||||
background-position: right -81px !important;
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
border-radius: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.filter-list {
|
||||
margin: 0 -20px;
|
||||
|
||||
.list-footer {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.added-filter-list {
|
||||
margin: 0 -20px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
$isCompound = $condition->isCompound();
|
||||
$isRoot = $treeLevel == 0;
|
||||
$collapsed = $this->getCollapseStatus($condition->id, false);
|
||||
?>
|
||||
<li
|
||||
id="<?= 'condition'.$condition->id ?>"
|
||||
data-condition-id="<?= $condition->id ?>"
|
||||
class="condition-item <?= $isCompound ? 'is-compound' : 'is-single' ?> <?= $isRoot ? 'is-root' : 'is-inner' ?> <?= $collapsed ? 'collapsed' : null ?>">
|
||||
|
||||
<?php if ($isCompound): ?>
|
||||
|
||||
<div class="compound-content">
|
||||
<h4>
|
||||
<a
|
||||
class="condition-collapse"
|
||||
href="javascript:;"
|
||||
title="Collapse / Expand"
|
||||
data-conditions-collapse>
|
||||
<i class="icon-chevron-up"></i>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="condition-text"
|
||||
data-conditions-settings>
|
||||
<?= e($this->getCacheConditionText($condition)) ?>
|
||||
</a>
|
||||
<?php if (!$isRoot): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
title="<?= e(__('Delete Condition')) ?>"
|
||||
class="condition-delete condition-delete-compound close"
|
||||
data-conditions-delete>
|
||||
<span aria-hidden="true">×</span>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</h4>
|
||||
<ul class="condition-set">
|
||||
<?php
|
||||
$children = $condition->children()->withDeferred($this->sessionKey.'_'.$condition->id)->get();
|
||||
$lastIndex = $children->count() - 1;
|
||||
?>
|
||||
<?php foreach ($children as $index => $childCondition): ?>
|
||||
<?= $this->makePartial('condition', [
|
||||
'condition' => $childCondition,
|
||||
'treeLevel' => $treeLevel + 1,
|
||||
'parentCondition' => $condition
|
||||
]) ?>
|
||||
<?php if ($index < $lastIndex): ?>
|
||||
<li class="compound-join">
|
||||
<a href="javascript:;" data-conditions-settings>
|
||||
<?= e($this->getCacheConditionJoinText($condition)) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php endforeach?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="compound-add-item loading-indicator-container indicator-center">
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-conditions-create
|
||||
data-request="onLoadConditionChildSelector">
|
||||
<?= e(__('Add Condition')) ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="condition-text">
|
||||
<a href="javascript:;" data-conditions-settings>
|
||||
<?= $this->getCacheConditionText($condition) ?>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
href="javascript:;"
|
||||
title="<?= e(__('Delete Condition')) ?>"
|
||||
class="condition-delete close"
|
||||
data-conditions-delete>
|
||||
<span aria-hidden="true">×</span>
|
||||
</a>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="condition_data[<?= $condition->id ?>]"
|
||||
value="<?= e(json_encode($this->getCacheConditionData($condition))) ?>" />
|
||||
|
||||
<?php if (isset($parentCondition)): ?>
|
||||
<input
|
||||
type="hidden"
|
||||
name="condition_parent_id[<?= $condition->id ?>]"
|
||||
value="<?= $parentCondition->id ?>" />
|
||||
<?php endif ?>
|
||||
|
||||
</li>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?= Form::open(['id' => 'propertyForm']) ?>
|
||||
|
||||
<div class="modal-header flex-row-reverse">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title">
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<?= e($condition->getTitle()) ?>
|
||||
<?php else: ?>
|
||||
Condition
|
||||
<?php endif ?>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<input type="hidden" name="current_parent_id" value="<?= $this->getParentIdFromCondition($condition) ?>" />
|
||||
<input type="hidden" name="current_condition_id" value="<?= $condition->id ?>" />
|
||||
<input type="hidden" name="current_condition_data" value="<?= e(json_encode($this->getCacheConditionDataPayload())) ?>" />
|
||||
|
||||
<div class="modal-body">
|
||||
<?= $conditionFormWidget->render() ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
data-request="<?= $this->getEventHandler('onSaveCondition') ?>"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-popup-load-indicator
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
|
||||
<?php if (!$this->isRootCondition($condition)): ?>
|
||||
<button
|
||||
type="button"
|
||||
class="oc-icon-trash-o btn-icon danger pull-right"
|
||||
data-request="<?= $this->getEventHandler('onDeleteCondition') ?>"
|
||||
data-popup-load-indicator
|
||||
data-request-confirm="<?= e(__('Do you really want to delete this condition?')) ?>">
|
||||
</button>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php Form::close() ?>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<ul class="condition-set">
|
||||
<?= $this->makePartial('condition', [
|
||||
'condition' => $condition,
|
||||
'treeLevel' => 0
|
||||
]) ?>
|
||||
</ul>
|
||||
|
||||
<?php if (isset($newConditionId)): ?>
|
||||
<script> showConditionSettings('<?= e($newConditionId) ?>') </script>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<div class="conditions-container">
|
||||
<div
|
||||
id="<?= $this->getId() ?>"
|
||||
class="control-ruleconditions"
|
||||
data-collapse-handler="<?= $this->getEventHandler('onSetCollapseStatus') ?>"
|
||||
data-settings-handler="<?= $this->getEventHandler('onLoadConditionSetup') ?>"
|
||||
data-delete-handler="<?= $this->getEventHandler('onDeleteCondition') ?>"
|
||||
data-cancel-handler="<?= $this->getEventHandler('onCancelConditionSettings') ?>"
|
||||
data-create-handler="<?= $this->getEventHandler('onLoadCreateChildCondition') ?>"
|
||||
data-control="ruleconditions">
|
||||
<?= $this->makePartial('conditions', ['condition' => $rootCondition]) ?>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="<?= $name ?>" value="" />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?= Form::open(['id' => 'propertyForm']) ?>
|
||||
|
||||
<div class="modal-header flex-row-reverse">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(__('Condition')) ?></h4>
|
||||
</div>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<input type="hidden" name="current_condition_id" value="<?= $condition->id ?>"/>
|
||||
<input type="hidden" name="current_condition_data" value="<?= e(json_encode($this->getCacheConditionDataPayload())) ?>" />
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group condition-class">
|
||||
<label class="form-label"><?= e(__('Condition')) ?></label>
|
||||
<select
|
||||
class="form-control custom-select"
|
||||
data-placeholder="<?= e(__('Please select a condition')) ?>"
|
||||
name="condition_class">
|
||||
<option value=""></option>
|
||||
<?php foreach ($options as $name => $class): ?>
|
||||
<?php if (!is_array($class)): ?>
|
||||
<option value="<?= $class ?>"><?= e($name) ?></option>
|
||||
<?php else: ?>
|
||||
<optgroup label="<?= e($name) ?>">
|
||||
<?php foreach ($class as $subName => $subClass): ?>
|
||||
<option value="<?= $subClass ?>"><?= e($subName) ?></option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
data-request="<?= $this->getEventHandler('onCreateCondition') ?>"
|
||||
data-popup-load-indicator
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php namespace RainLab\Notify\Interfaces;
|
||||
|
||||
/**
|
||||
* This contract represents a notification action.
|
||||
*/
|
||||
interface Action
|
||||
{
|
||||
/**
|
||||
* Returns a action text summary when displaying to the user.
|
||||
* @return string
|
||||
*/
|
||||
public function getText();
|
||||
|
||||
/**
|
||||
* Returns a action title for displaying in the action settings form.
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Returns information about this action, including name and description.
|
||||
*/
|
||||
public function actionDetails();
|
||||
|
||||
/**
|
||||
* Triggers this action.
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function triggerAction($params);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php namespace RainLab\Notify\Interfaces;
|
||||
|
||||
/**
|
||||
* This contract represents a Compound Condition rule.
|
||||
*/
|
||||
interface CompoundCondition
|
||||
{
|
||||
/**
|
||||
* Returns the text to use when joining two rules within.
|
||||
* @return string
|
||||
*/
|
||||
public function getJoinText();
|
||||
|
||||
/**
|
||||
* Returns a list of condition types (`ConditionBase::TYPE_*` constants)
|
||||
* that can be added to this compound condition
|
||||
*/
|
||||
public function getAllowedSubtypes();
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php namespace RainLab\Notify\Interfaces;
|
||||
|
||||
/**
|
||||
* This contract represents a Compound Condition rule.
|
||||
*/
|
||||
interface Condition
|
||||
{
|
||||
/**
|
||||
* Returns a condition text summary when displaying to the user.
|
||||
* @return string
|
||||
*/
|
||||
public function getText();
|
||||
|
||||
/**
|
||||
* Returns a condition title for displaying in the condition settings form
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Returns a list of options supported beneth this condition.
|
||||
*
|
||||
* Options can contain these keys:
|
||||
* - ruleType: Rule type as specified by ConditionBase::TYPE_* constants
|
||||
* - parentIds: An array of parent ids to constrain child conditions
|
||||
* - extraRules: An array of additional condition classes to use
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getChildOptions(array $options);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php namespace RainLab\Notify\Interfaces;
|
||||
|
||||
/**
|
||||
* This contract represents a notification event.
|
||||
*/
|
||||
interface Event
|
||||
{
|
||||
/**
|
||||
* Returns information about this event, including name and description.
|
||||
* @return array
|
||||
*/
|
||||
public function eventDetails();
|
||||
|
||||
/**
|
||||
* Generates event parameters based on arguments from the triggering system event.
|
||||
* @param array $args
|
||||
* @param string $eventName
|
||||
* @return void
|
||||
*/
|
||||
public static function makeParamsFromEvent(array $args, $eventName = null);
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "يخطر",
|
||||
"Notification Services": "خدمات الإخطار",
|
||||
"Notification Rules": "قواعد الإخطار",
|
||||
"Manage the events and actions that trigger notifications.": "إدارة الأحداث والإجراءات التي تؤدي إلى تشغيل الإخطارات.",
|
||||
"Name": "اسم",
|
||||
"Code": "رمز",
|
||||
"Notification Rule": "قاعدة الإخطار",
|
||||
"Add Notification Rule": "أضف قاعدة الإخطار",
|
||||
"Schedule": "برنامج",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "لاحظ أن الجدولة قد لا تعمل مع تكوينات معينة لبرنامج تشغيل قائمة الانتظار.",
|
||||
"Learn more": "يتعلم أكثر",
|
||||
"The configured queue driver does not support delayed execution.": "لا يدعم برنامج تشغيل قائمة الانتظار المكون التنفيذ المؤجل.",
|
||||
"Notifications management": "إدارة الإخطارات",
|
||||
"Return to Notifications": "العودة إلى الإخطارات",
|
||||
"Are you sure?": "هل أنت واثق؟",
|
||||
"Action": "عمل",
|
||||
"Action Description": "وصف الإجراء",
|
||||
"Compound Condition": "الحالة المركبة",
|
||||
"ALL of subconditions should be": "يجب أن تكون جميع الشروط الفرعية",
|
||||
"ANY of subconditions should be": "يجب أن تكون أي من الشروط الفرعية",
|
||||
"ON": "على",
|
||||
"AND": "و",
|
||||
"OR": "أو",
|
||||
"ALL subconditions should meet the requirement": "يجب أن تفي جميع الشروط الفرعية بالمتطلبات",
|
||||
"ANY subconditions should meet the requirement": "يجب أن تفي أي شروط فرعية بالمتطلبات",
|
||||
"Condition Text": "نص الشرط",
|
||||
"Condition": "شرط",
|
||||
"Event": "هدف",
|
||||
"Event description": "وصف الحدث",
|
||||
"is": "يكون",
|
||||
"is not": "ليس",
|
||||
"equals or greater than": "يساوي أو أكبر من",
|
||||
"equals or less than": "يساوي أو أقل من",
|
||||
"contains": "يحتوي على",
|
||||
"does not contain": "لا يحتوي",
|
||||
"greater than": "أكثر من",
|
||||
"less than": "أقل من",
|
||||
"is one of": "هو واحد من",
|
||||
"is not one of": "ليس واحدًا من",
|
||||
"Unknown Attribute": "سمة غير معروفة",
|
||||
"Unknown attribute selected": "تم تحديد سمة غير معروفة",
|
||||
"Condition Type": "نوع الشرط",
|
||||
"Required Value": "قيمة مطلوبة",
|
||||
"Selected Records": "سجلات مختارة",
|
||||
"No records added": "لا توجد سجلات مضافة",
|
||||
"Attribute": "يصف",
|
||||
"Operator": "المشغل أو العامل",
|
||||
"Value": "قيمة",
|
||||
"Delete this notification rule?": "هل تريد حذف قاعدة الإخطار هذه؟",
|
||||
"New Notification Rule": "قاعدة الإخطار الجديدة",
|
||||
"Do you really want to delete this action?": "هل تريد حقًا حذف هذا الإجراء؟",
|
||||
"Add Action": "أضف إجراء",
|
||||
"These variables are available": "هذه المتغيرات متوفرة",
|
||||
"Click or drag these in to the content area": "انقر أو اسحب هذه إلى منطقة المحتوى",
|
||||
"This action does not provide any variables.": "لا يوفر هذا الإجراء أي متغيرات.",
|
||||
"Delete Condition": "حذف الشرط",
|
||||
"Add Condition": "أضف شرط",
|
||||
"Do you really want to delete this condition?": "هل تريد حقا حذف هذا الشرط؟",
|
||||
"Please select a condition": "الرجاء تحديد الشرط"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Паведаміць",
|
||||
"Notification Services": "Паслугі апавяшчэнняў",
|
||||
"Notification Rules": "Правілы паведамлення",
|
||||
"Manage the events and actions that trigger notifications.": "Кіруйце падзеямі і дзеяннямі, якія выклікаюць апавяшчэнні.",
|
||||
"Name": "Імя",
|
||||
"Code": "Код",
|
||||
"Notification Rule": "Правіла паведамлення",
|
||||
"Add Notification Rule": "Дадаць правіла апавяшчэнняў",
|
||||
"Schedule": "Расклад",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Звярніце ўвагу, што планаванне можа не працаваць для некаторых канфігурацый драйвера чаргі.",
|
||||
"Learn more": "Даведайцеся больш",
|
||||
"The configured queue driver does not support delayed execution.": "Настроены драйвер чаргі не падтрымлівае адкладзенае выкананне.",
|
||||
"Notifications management": "Кіраванне апавяшчэннямі",
|
||||
"Return to Notifications": "Вярнуцца да апавяшчэнняў",
|
||||
"Are you sure?": "Вы ўпэўнены?",
|
||||
"Action": "Дзеянне",
|
||||
"Action Description": "Апісанне дзеяння",
|
||||
"Compound Condition": "Складаны стан",
|
||||
"ALL of subconditions should be": "УСЕ падумовы павінны быць",
|
||||
"ANY of subconditions should be": "ЛЮБОЕ з падумоў павінна быць",
|
||||
"ON": "ВКЛ",
|
||||
"AND": "І",
|
||||
"OR": "АБО",
|
||||
"ALL subconditions should meet the requirement": "УСЕ падумовы павінны адпавядаць патрабаванню",
|
||||
"ANY subconditions should meet the requirement": "ЛЮБЫЯ падумовы павінны адпавядаць патрабаванню",
|
||||
"Condition Text": "Тэкст стану",
|
||||
"Condition": "Стан",
|
||||
"Event": "Падзея",
|
||||
"Event description": "Апісанне падзеі",
|
||||
"is": "ёсць",
|
||||
"is not": "не",
|
||||
"equals or greater than": "роўна або больш",
|
||||
"equals or less than": "роўна або менш",
|
||||
"contains": "змяшчае",
|
||||
"does not contain": "не ўтрымлівае",
|
||||
"greater than": "больш, чым",
|
||||
"less than": "менш чым",
|
||||
"is one of": "з'яўляецца адным з",
|
||||
"is not one of": "не з'яўляецца адным з",
|
||||
"Unknown Attribute": "Невядомы атрыбут",
|
||||
"Unknown attribute selected": "Выбраны невядомы атрыбут",
|
||||
"Condition Type": "Тып стану",
|
||||
"Required Value": "Неабходнае значэнне",
|
||||
"Selected Records": "Выбраныя запісы",
|
||||
"No records added": "Запісаў не дададзены",
|
||||
"Attribute": "Атрыбут",
|
||||
"Operator": "Аператар",
|
||||
"Value": "Значэнне",
|
||||
"Delete this notification rule?": "Выдаліць гэта правіла апавяшчэнняў?",
|
||||
"New Notification Rule": "Новае правіла апавяшчэнняў",
|
||||
"Do you really want to delete this action?": "Вы сапраўды хочаце выдаліць гэта дзеянне?",
|
||||
"Add Action": "Дадаць дзеянне",
|
||||
"These variables are available": "Гэтыя зменныя даступныя",
|
||||
"Click or drag these in to the content area": "Націсніце або перацягніце іх у вобласць змесціва",
|
||||
"This action does not provide any variables.": "Гэта дзеянне не дае ніякіх зменных.",
|
||||
"Delete Condition": "Умова выдалення",
|
||||
"Add Condition": "Дадаць умову",
|
||||
"Do you really want to delete this condition?": "Вы сапраўды хочаце выдаліць гэтую ўмову?",
|
||||
"Please select a condition": "Выберыце ўмову"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Уведомете",
|
||||
"Notification Services": "Уведомителни услуги",
|
||||
"Notification Rules": "Правила за уведомяване",
|
||||
"Manage the events and actions that trigger notifications.": "Управлявайте събитията и действията, които задействат известия.",
|
||||
"Name": "име",
|
||||
"Code": "код",
|
||||
"Notification Rule": "Правило за уведомяване",
|
||||
"Add Notification Rule": "Добавете правило за уведомяване",
|
||||
"Schedule": "График",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Имайте предвид, че планирането може да не работи за определени конфигурации на драйвери на опашката.",
|
||||
"Learn more": "Научете повече",
|
||||
"The configured queue driver does not support delayed execution.": "Конфигурираният драйвер за опашка не поддържа отложено изпълнение.",
|
||||
"Notifications management": "Управление на известията",
|
||||
"Return to Notifications": "Върнете се към Известия",
|
||||
"Are you sure?": "Сигурен ли си?",
|
||||
"Action": "Действие",
|
||||
"Action Description": "Описание на действието",
|
||||
"Compound Condition": "Сложно състояние",
|
||||
"ALL of subconditions should be": "ВСИЧКИ подусловия трябва да бъдат",
|
||||
"ANY of subconditions should be": "ВСЯКО от подусловията трябва да бъде",
|
||||
"ON": "НА",
|
||||
"AND": "И",
|
||||
"OR": "ИЛИ",
|
||||
"ALL subconditions should meet the requirement": "ВСИЧКИ подусловия трябва да отговарят на изискването",
|
||||
"ANY subconditions should meet the requirement": "ВСЕКИ подусловия трябва да отговарят на изискването",
|
||||
"Condition Text": "Текст на условието",
|
||||
"Condition": "Състояние",
|
||||
"Event": "Събитие",
|
||||
"Event description": "Описание на събитието",
|
||||
"is": "е",
|
||||
"is not": "не е",
|
||||
"equals or greater than": "равно или по-голямо от",
|
||||
"equals or less than": "равно или по-малко от",
|
||||
"contains": "съдържа",
|
||||
"does not contain": "не съдържа",
|
||||
"greater than": "по-голяма от",
|
||||
"less than": "по-малко от",
|
||||
"is one of": "е един от",
|
||||
"is not one of": "не е един от",
|
||||
"Unknown Attribute": "Неизвестен атрибут",
|
||||
"Unknown attribute selected": "Избран е неизвестен атрибут",
|
||||
"Condition Type": "Тип на състояние",
|
||||
"Required Value": "Задължителна стойност",
|
||||
"Selected Records": "Избрани записи",
|
||||
"No records added": "Няма добавени записи",
|
||||
"Attribute": "Атрибут",
|
||||
"Operator": "Оператор",
|
||||
"Value": "Стойност",
|
||||
"Delete this notification rule?": "Да се изтрие ли това правило за уведомяване?",
|
||||
"New Notification Rule": "Ново правило за уведомяване",
|
||||
"Do you really want to delete this action?": "Наистина ли искате да изтриете това действие?",
|
||||
"Add Action": "Добавяне на действие",
|
||||
"These variables are available": "Тези променливи са налични",
|
||||
"Click or drag these in to the content area": "Щракнете или плъзнете ги в областта на съдържанието",
|
||||
"This action does not provide any variables.": "Това действие не предоставя никакви променливи.",
|
||||
"Delete Condition": "Условие за изтриване",
|
||||
"Add Condition": "Добавете условие",
|
||||
"Do you really want to delete this condition?": "Наистина ли искате да изтриете това условие?",
|
||||
"Please select a condition": "Моля, изберете условие"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Oznámit",
|
||||
"Notification Services": "Notifikační služby",
|
||||
"Notification Rules": "Pravidla oznamování",
|
||||
"Manage the events and actions that trigger notifications.": "Spravujte události a akce, které spouštějí oznámení.",
|
||||
"Name": "název",
|
||||
"Code": "Kód",
|
||||
"Notification Rule": "Pravidlo oznámení",
|
||||
"Add Notification Rule": "Přidat pravidlo oznámení",
|
||||
"Schedule": "Plán",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "U některých konfigurací ovladače fronty nemusí plánování fungovat.",
|
||||
"Learn more": "Zjistěte více",
|
||||
"The configured queue driver does not support delayed execution.": "Nakonfigurovaný ovladač fronty nepodporuje zpožděné spuštění.",
|
||||
"Notifications management": "Správa oznámení",
|
||||
"Return to Notifications": "Vraťte se do Oznámení",
|
||||
"Are you sure?": "Jsi si jistá?",
|
||||
"Action": "Akce",
|
||||
"Action Description": "Popis akce",
|
||||
"Compound Condition": "Složený stav",
|
||||
"ALL of subconditions should be": "VŠECHNY dílčí podmínky by měly být",
|
||||
"ANY of subconditions should be": "KTERÁKOLI podmínka by měla být",
|
||||
"ON": "NA",
|
||||
"AND": "A",
|
||||
"OR": "NEBO",
|
||||
"ALL subconditions should meet the requirement": "VŠECHNY dílčí podmínky by měly splňovat požadavek",
|
||||
"ANY subconditions should meet the requirement": "ŽÁDNÉ dílčí podmínky by měly splňovat požadavek",
|
||||
"Condition Text": "Podmínka Text",
|
||||
"Condition": "Stav",
|
||||
"Event": "událost",
|
||||
"Event description": "Popis události",
|
||||
"is": "je",
|
||||
"is not": "není",
|
||||
"equals or greater than": "se rovná nebo je větší než",
|
||||
"equals or less than": "se rovná nebo je menší než",
|
||||
"contains": "obsahuje",
|
||||
"does not contain": "neobsahuje",
|
||||
"greater than": "větší než",
|
||||
"less than": "méně než",
|
||||
"is one of": "je jedním z",
|
||||
"is not one of": "není jedním z",
|
||||
"Unknown Attribute": "Neznámý atribut",
|
||||
"Unknown attribute selected": "Vybrán neznámý atribut",
|
||||
"Condition Type": "Typ podmínky",
|
||||
"Required Value": "Požadovaná hodnota",
|
||||
"Selected Records": "Vybrané záznamy",
|
||||
"No records added": "Nebyly přidány žádné záznamy",
|
||||
"Attribute": "Atribut",
|
||||
"Operator": "Operátor",
|
||||
"Value": "Hodnota",
|
||||
"Delete this notification rule?": "Smazat toto pravidlo oznámení?",
|
||||
"New Notification Rule": "Nové pravidlo oznamování",
|
||||
"Do you really want to delete this action?": "Opravdu chcete tuto akci smazat?",
|
||||
"Add Action": "Přidat akci",
|
||||
"These variables are available": "Tyto proměnné jsou k dispozici",
|
||||
"Click or drag these in to the content area": "Klikněte na ně nebo je přetáhněte do oblasti obsahu",
|
||||
"This action does not provide any variables.": "Tato akce neposkytuje žádné proměnné.",
|
||||
"Delete Condition": "Smazat podmínku",
|
||||
"Add Condition": "Přidat podmínku",
|
||||
"Do you really want to delete this condition?": "Opravdu chcete smazat tuto podmínku?",
|
||||
"Please select a condition": "Vyberte prosím podmínku"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Underrette",
|
||||
"Notification Services": "Underretningstjenester",
|
||||
"Notification Rules": "Underretningsregler",
|
||||
"Manage the events and actions that trigger notifications.": "Administrer de begivenheder og handlinger, der udløser notifikationer.",
|
||||
"Name": "Navn",
|
||||
"Code": "Kode",
|
||||
"Notification Rule": "Underretningsregel",
|
||||
"Add Notification Rule": "Tilføj meddelelsesregel",
|
||||
"Schedule": "Tidsplan",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Bemærk, at planlægning muligvis ikke virker for visse kødriverkonfigurationer.",
|
||||
"Learn more": "Lær mere",
|
||||
"The configured queue driver does not support delayed execution.": "Den konfigurerede kødriver understøtter ikke forsinket udførelse.",
|
||||
"Notifications management": "Administration af notifikationer",
|
||||
"Return to Notifications": "Vend tilbage til meddelelser",
|
||||
"Are you sure?": "Er du sikker?",
|
||||
"Action": "Handling",
|
||||
"Action Description": "Handlingsbeskrivelse",
|
||||
"Compound Condition": "Sammensat tilstand",
|
||||
"ALL of subconditions should be": "ALLE underbetingelser bør være",
|
||||
"ANY of subconditions should be": "ENHVER af underbetingelser bør være",
|
||||
"ON": "PÅ",
|
||||
"AND": "OG",
|
||||
"OR": "ELLER",
|
||||
"ALL subconditions should meet the requirement": "ALLE underbetingelser bør opfylde kravet",
|
||||
"ANY subconditions should meet the requirement": "ALLE underbetingelser bør opfylde kravet",
|
||||
"Condition Text": "Tilstandstekst",
|
||||
"Condition": "Tilstand",
|
||||
"Event": "Begivenhed",
|
||||
"Event description": "Begivenhedsbeskrivelse",
|
||||
"is": "er",
|
||||
"is not": "er ikke",
|
||||
"equals or greater than": "lig med eller større end",
|
||||
"equals or less than": "lig med eller mindre end",
|
||||
"contains": "indeholder",
|
||||
"does not contain": "indeholder ikke",
|
||||
"greater than": "bedre end",
|
||||
"less than": "Mindre end",
|
||||
"is one of": "er en af",
|
||||
"is not one of": "er ikke en af",
|
||||
"Unknown Attribute": "Ukendt attribut",
|
||||
"Unknown attribute selected": "Ukendt attribut valgt",
|
||||
"Condition Type": "Tilstandstype",
|
||||
"Required Value": "Påkrævet værdi",
|
||||
"Selected Records": "Udvalgte poster",
|
||||
"No records added": "Ingen registreringer tilføjet",
|
||||
"Attribute": "Attribut",
|
||||
"Operator": "Operatør",
|
||||
"Value": "Værdi",
|
||||
"Delete this notification rule?": "Vil du slette denne underretningsregel?",
|
||||
"New Notification Rule": "Ny meddelelsesregel",
|
||||
"Do you really want to delete this action?": "Vil du virkelig slette denne handling?",
|
||||
"Add Action": "Tilføj handling",
|
||||
"These variables are available": "Disse variabler er tilgængelige",
|
||||
"Click or drag these in to the content area": "Klik eller træk disse ind til indholdsområdet",
|
||||
"This action does not provide any variables.": "Denne handling giver ingen variabler.",
|
||||
"Delete Condition": "Slet tilstand",
|
||||
"Add Condition": "Tilføj tilstand",
|
||||
"Do you really want to delete this condition?": "Vil du virkelig slette denne betingelse?",
|
||||
"Please select a condition": "Vælg venligst en betingelse"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Benachrichtigen",
|
||||
"Notification Services": "Benachrichtigungsdienste",
|
||||
"Notification Rules": "Benachrichtigungsregeln",
|
||||
"Manage the events and actions that trigger notifications.": "Verwalten Sie die Ereignisse und Aktionen, die Benachrichtigungen auslösen.",
|
||||
"Name": "Name",
|
||||
"Code": "Code",
|
||||
"Notification Rule": "Benachrichtigungsregel",
|
||||
"Add Notification Rule": "Benachrichtigungsregel hinzufügen",
|
||||
"Schedule": "Zeitplan",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Beachten Sie, dass die Planung für bestimmte Warteschlangentreiberkonfigurationen möglicherweise nicht funktioniert.",
|
||||
"Learn more": "Mehr erfahren",
|
||||
"The configured queue driver does not support delayed execution.": "Der konfigurierte Warteschlangentreiber unterstützt keine verzögerte Ausführung.",
|
||||
"Notifications management": "Benachrichtigungsverwaltung",
|
||||
"Return to Notifications": "Zurück zu Benachrichtigungen",
|
||||
"Are you sure?": "Bist du sicher?",
|
||||
"Action": "Aktion",
|
||||
"Action Description": "Aktionsbeschreibung",
|
||||
"Compound Condition": "Zusammengesetzter Zustand",
|
||||
"ALL of subconditions should be": "ALLE Unterbedingungen sollten sein",
|
||||
"ANY of subconditions should be": "JEDE der Unterbedingungen sollte sein",
|
||||
"ON": "AN",
|
||||
"AND": "UND",
|
||||
"OR": "ODER",
|
||||
"ALL subconditions should meet the requirement": "ALLE Unterbedingungen sollten die Anforderung erfüllen",
|
||||
"ANY subconditions should meet the requirement": "ALLE Unterbedingungen sollten die Anforderung erfüllen",
|
||||
"Condition Text": "Bedingungstext",
|
||||
"Condition": "Zustand",
|
||||
"Event": "Fall",
|
||||
"Event description": "Eventbeschreibung",
|
||||
"is": "ist",
|
||||
"is not": "ist nicht",
|
||||
"equals or greater than": "gleich oder größer als",
|
||||
"equals or less than": "gleich oder kleiner als",
|
||||
"contains": "enthält",
|
||||
"does not contain": "beinhaltet nicht",
|
||||
"greater than": "größer als",
|
||||
"less than": "weniger als",
|
||||
"is one of": "ist einer von",
|
||||
"is not one of": "ist keiner von",
|
||||
"Unknown Attribute": "Unbekanntes Attribut",
|
||||
"Unknown attribute selected": "Unbekanntes Attribut ausgewählt",
|
||||
"Condition Type": "Bedingungstyp",
|
||||
"Required Value": "Erforderlicher Wert",
|
||||
"Selected Records": "Ausgewählte Aufzeichnungen",
|
||||
"No records added": "Keine Datensätze hinzugefügt",
|
||||
"Attribute": "Attribut",
|
||||
"Operator": "Operator",
|
||||
"Value": "Wert",
|
||||
"Delete this notification rule?": "Diese Benachrichtigungsregel löschen?",
|
||||
"New Notification Rule": "Neue Benachrichtigungsregel",
|
||||
"Do you really want to delete this action?": "Möchten Sie diese Aktion wirklich löschen?",
|
||||
"Add Action": "Aktion hinzufügen",
|
||||
"These variables are available": "Diese Variablen sind verfügbar",
|
||||
"Click or drag these in to the content area": "Klicken oder ziehen Sie diese in den Inhaltsbereich",
|
||||
"This action does not provide any variables.": "Diese Aktion stellt keine Variablen bereit.",
|
||||
"Delete Condition": "Bedingung löschen",
|
||||
"Add Condition": "Bedingung hinzufügen",
|
||||
"Do you really want to delete this condition?": "Möchten Sie diese Bedingung wirklich löschen?",
|
||||
"Please select a condition": "Bitte wählen Sie eine Bedingung aus"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Notificar",
|
||||
"Notification Services": "Servicios de notificación",
|
||||
"Notification Rules": "Reglas de notificación",
|
||||
"Manage the events and actions that trigger notifications.": "Administre los eventos y acciones que activan las notificaciones.",
|
||||
"Name": "Nombre",
|
||||
"Code": "Código",
|
||||
"Notification Rule": "Regla de notificación",
|
||||
"Add Notification Rule": "Agregar regla de notificación",
|
||||
"Schedule": "Calendario",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Tenga en cuenta que es posible que la programación no funcione para ciertas configuraciones de controladores de cola.",
|
||||
"Learn more": "Aprende más",
|
||||
"The configured queue driver does not support delayed execution.": "El controlador de cola configurado no admite la ejecución retrasada.",
|
||||
"Notifications management": "Gestión de notificaciones",
|
||||
"Return to Notifications": "Volver a Notificaciones",
|
||||
"Are you sure?": "¿Está seguro?",
|
||||
"Action": "Acción",
|
||||
"Action Description": "Acción Descripción",
|
||||
"Compound Condition": "Condición compuesta",
|
||||
"ALL of subconditions should be": "TODAS las subcondiciones deben ser",
|
||||
"ANY of subconditions should be": "CUALQUIERA de las subcondiciones debe ser",
|
||||
"ON": "EN",
|
||||
"AND": "Y",
|
||||
"OR": "O",
|
||||
"ALL subconditions should meet the requirement": "TODAS las subcondiciones deben cumplir con el requisito",
|
||||
"ANY subconditions should meet the requirement": "CUALQUIER subcondición debe cumplir con el requisito",
|
||||
"Condition Text": "Texto de condición",
|
||||
"Condition": "Condición",
|
||||
"Event": "Evento",
|
||||
"Event description": "Descripción del evento",
|
||||
"is": "es",
|
||||
"is not": "no es",
|
||||
"equals or greater than": "igual o mayor que",
|
||||
"equals or less than": "igual o menor que",
|
||||
"contains": "contiene",
|
||||
"does not contain": "no contiene",
|
||||
"greater than": "mas grande que",
|
||||
"less than": "menos que",
|
||||
"is one of": "es uno de",
|
||||
"is not one of": "no es uno de",
|
||||
"Unknown Attribute": "Atributo desconocido",
|
||||
"Unknown attribute selected": "Atributo desconocido seleccionado",
|
||||
"Condition Type": "Tipo de condición",
|
||||
"Required Value": "Valor requerido",
|
||||
"Selected Records": "Registros seleccionados",
|
||||
"No records added": "No se agregaron registros",
|
||||
"Attribute": "Atributo",
|
||||
"Operator": "Operador",
|
||||
"Value": "Valor",
|
||||
"Delete this notification rule?": "¿Eliminar esta regla de notificación?",
|
||||
"New Notification Rule": "Nueva regla de notificación",
|
||||
"Do you really want to delete this action?": "¿Realmente desea eliminar esta acción?",
|
||||
"Add Action": "Agregar acción",
|
||||
"These variables are available": "Estas variables están disponibles",
|
||||
"Click or drag these in to the content area": "Haga clic o arrástrelos al área de contenido",
|
||||
"This action does not provide any variables.": "Esta acción no proporciona ninguna variable.",
|
||||
"Delete Condition": "Eliminar condición",
|
||||
"Add Condition": "Añadir condición",
|
||||
"Do you really want to delete this condition?": "¿Realmente desea eliminar esta condición?",
|
||||
"Please select a condition": "Seleccione una condición"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Teavita",
|
||||
"Notification Services": "Teavitusteenused",
|
||||
"Notification Rules": "Teavitamise reeglid",
|
||||
"Manage the events and actions that trigger notifications.": "Hallake märguandeid käivitavaid sündmusi ja toiminguid.",
|
||||
"Name": "Nimi",
|
||||
"Code": "Kood",
|
||||
"Notification Rule": "Teavitamise reegel",
|
||||
"Add Notification Rule": "Lisa teavitusreegel",
|
||||
"Schedule": "Ajakava",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Pange tähele, et ajastamine ei pruugi teatud järjekorra draiveri konfiguratsioonide puhul töötada.",
|
||||
"Learn more": "Lisateavet",
|
||||
"The configured queue driver does not support delayed execution.": "Konfigureeritud järjekorra draiver ei toeta viivitatud täitmist.",
|
||||
"Notifications management": "Teatiste haldamine",
|
||||
"Return to Notifications": "Naaske jaotisesse Teatised",
|
||||
"Are you sure?": "Oled sa kindel?",
|
||||
"Action": "Tegevus",
|
||||
"Action Description": "Toimingu kirjeldus",
|
||||
"Compound Condition": "Liitseisund",
|
||||
"ALL of subconditions should be": "KÕIK alamtingimused peaksid olema",
|
||||
"ANY of subconditions should be": "Mistahes alamtingimused peaksid olema",
|
||||
"ON": "PEAL",
|
||||
"AND": "JA",
|
||||
"OR": "VÕI",
|
||||
"ALL subconditions should meet the requirement": "KÕIK alamtingimused peaksid vastama nõuetele",
|
||||
"ANY subconditions should meet the requirement": "KÕIK alamtingimused peaksid nõuetele vastama",
|
||||
"Condition Text": "Seisundi tekst",
|
||||
"Condition": "Seisund",
|
||||
"Event": "Sündmus",
|
||||
"Event description": "Sündmuse kirjeldus",
|
||||
"is": "on",
|
||||
"is not": "ei ole",
|
||||
"equals or greater than": "võrdne või suurem kui",
|
||||
"equals or less than": "võrdne või väiksem kui",
|
||||
"contains": "sisaldab",
|
||||
"does not contain": "ei sisalda",
|
||||
"greater than": "suurem kui",
|
||||
"less than": "vähem kui",
|
||||
"is one of": "on üks",
|
||||
"is not one of": "ei ole üks",
|
||||
"Unknown Attribute": "Tundmatu atribuut",
|
||||
"Unknown attribute selected": "Valitud on tundmatu atribuut",
|
||||
"Condition Type": "Seisundi tüüp",
|
||||
"Required Value": "Nõutav väärtus",
|
||||
"Selected Records": "Valitud kirjed",
|
||||
"No records added": "Kirjeid pole lisatud",
|
||||
"Attribute": "Atribuut",
|
||||
"Operator": "Operaator",
|
||||
"Value": "Väärtus",
|
||||
"Delete this notification rule?": "Kas kustutada see teavitusreegel?",
|
||||
"New Notification Rule": "Uus teavitamise reegel",
|
||||
"Do you really want to delete this action?": "Kas soovite tõesti selle toimingu kustutada?",
|
||||
"Add Action": "Lisa toiming",
|
||||
"These variables are available": "Need muutujad on saadaval",
|
||||
"Click or drag these in to the content area": "Klõpsake või lohistage need sisualale",
|
||||
"This action does not provide any variables.": "See toiming ei paku muutujaid.",
|
||||
"Delete Condition": "Kustuta tingimus",
|
||||
"Add Condition": "Lisa tingimus",
|
||||
"Do you really want to delete this condition?": "Kas soovite tõesti selle tingimuse kustutada?",
|
||||
"Please select a condition": "Valige tingimus"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "اعلام کردن",
|
||||
"Notification Services": "خدمات اطلاع رسانی",
|
||||
"Notification Rules": "قوانین اطلاع رسانی",
|
||||
"Manage the events and actions that trigger notifications.": "رویدادها و اقداماتی را که اعلانها را راهاندازی میکنند مدیریت کنید.",
|
||||
"Name": "نام",
|
||||
"Code": "کد",
|
||||
"Notification Rule": "قانون اطلاع رسانی",
|
||||
"Add Notification Rule": "قانون اعلان را اضافه کنید",
|
||||
"Schedule": "برنامه",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "توجه داشته باشید که زمانبندی ممکن است برای پیکربندیهای درایور صف خاص کار نکند.",
|
||||
"Learn more": "بیشتر بدانید",
|
||||
"The configured queue driver does not support delayed execution.": "درایور صف پیکربندی شده از اجرای تاخیری پشتیبانی نمی کند.",
|
||||
"Notifications management": "مدیریت اعلان ها",
|
||||
"Return to Notifications": "بازگشت به اطلاعیه ها",
|
||||
"Are you sure?": "مطمئنی؟",
|
||||
"Action": "عمل",
|
||||
"Action Description": "توضیحات اقدام",
|
||||
"Compound Condition": "وضعیت مرکب",
|
||||
"ALL of subconditions should be": "همه شرایط فرعی باید باشد",
|
||||
"ANY of subconditions should be": "هر یک از شرایط فرعی باید باشد",
|
||||
"ON": "بر",
|
||||
"AND": "و",
|
||||
"OR": "یا",
|
||||
"ALL subconditions should meet the requirement": "همه شرایط فرعی باید الزامات را برآورده کنند",
|
||||
"ANY subconditions should meet the requirement": "هر شرط فرعی باید الزامات را برآورده کند",
|
||||
"Condition Text": "متن شرط",
|
||||
"Condition": "شرایط. شرط",
|
||||
"Event": "رویداد",
|
||||
"Event description": "شرح رویداد",
|
||||
"is": "است",
|
||||
"is not": "نیست",
|
||||
"equals or greater than": "مساوی یا بزرگتر از",
|
||||
"equals or less than": "برابر یا کمتر از",
|
||||
"contains": "شامل",
|
||||
"does not contain": "شامل نمی شود",
|
||||
"greater than": "بزرگتر از",
|
||||
"less than": "کمتر از",
|
||||
"is one of": "یکی از",
|
||||
"is not one of": "یکی از نیست",
|
||||
"Unknown Attribute": "ویژگی نامشخص",
|
||||
"Unknown attribute selected": "ویژگی ناشناخته انتخاب شد",
|
||||
"Condition Type": "نوع شرایط",
|
||||
"Required Value": "ارزش مورد نیاز",
|
||||
"Selected Records": "سوابق انتخاب شده",
|
||||
"No records added": "هیچ رکوردی اضافه نشد",
|
||||
"Attribute": "صفت",
|
||||
"Operator": "اپراتور",
|
||||
"Value": "مقدار",
|
||||
"Delete this notification rule?": "این قانون اعلان حذف شود؟",
|
||||
"New Notification Rule": "قانون جدید اطلاع رسانی",
|
||||
"Do you really want to delete this action?": "آیا واقعاً می خواهید این عمل را حذف کنید؟",
|
||||
"Add Action": "افزودن اکشن",
|
||||
"These variables are available": "این متغیرها در دسترس هستند",
|
||||
"Click or drag these in to the content area": "روی آنها کلیک کنید یا به قسمت محتوا بکشید",
|
||||
"This action does not provide any variables.": "این عمل هیچ متغیری ارائه نمی دهد.",
|
||||
"Delete Condition": "شرط حذف",
|
||||
"Add Condition": "اضافه کردن شرط",
|
||||
"Do you really want to delete this condition?": "آیا واقعاً می خواهید این شرط را حذف کنید؟",
|
||||
"Please select a condition": "لطفاً یک شرط را انتخاب کنید"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Notifier",
|
||||
"Notification Services": "Services de notification",
|
||||
"Notification Rules": "Règles de notification",
|
||||
"Manage the events and actions that trigger notifications.": "Gérez les événements et les actions qui déclenchent des notifications.",
|
||||
"Name": "Nom",
|
||||
"Code": "Code",
|
||||
"Notification Rule": "Règle de notification",
|
||||
"Add Notification Rule": "Ajouter une règle de notification",
|
||||
"Schedule": "Programme",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Notez que la planification peut ne pas fonctionner pour certaines configurations de pilote de file d'attente.",
|
||||
"Learn more": "Apprendre encore plus",
|
||||
"The configured queue driver does not support delayed execution.": "Le pilote de file d'attente configuré ne prend pas en charge l'exécution différée.",
|
||||
"Notifications management": "Gestion des notifications",
|
||||
"Return to Notifications": "Retour aux Notifications",
|
||||
"Are you sure?": "Êtes-vous sûr?",
|
||||
"Action": "action",
|
||||
"Action Description": "Description de l'action",
|
||||
"Compound Condition": "État composé",
|
||||
"ALL of subconditions should be": "TOUTES les sous-conditions doivent être",
|
||||
"ANY of subconditions should be": "TOUTES les sous-conditions doivent être",
|
||||
"ON": "AU",
|
||||
"AND": "ET",
|
||||
"OR": "OU",
|
||||
"ALL subconditions should meet the requirement": "TOUTES les sous-conditions doivent répondre à l'exigence",
|
||||
"ANY subconditions should meet the requirement": "TOUTES les sous-conditions doivent répondre à l'exigence",
|
||||
"Condition Text": "Texte de condition",
|
||||
"Condition": "État",
|
||||
"Event": "Événement",
|
||||
"Event description": "Description de l'évenement",
|
||||
"is": "est",
|
||||
"is not": "n'est pas",
|
||||
"equals or greater than": "égal ou supérieur à",
|
||||
"equals or less than": "égal ou inférieur à",
|
||||
"contains": "contient",
|
||||
"does not contain": "ne contient pas",
|
||||
"greater than": "plus grand que",
|
||||
"less than": "moins que",
|
||||
"is one of": "fait partie de",
|
||||
"is not one of": "n'est pas l'un des",
|
||||
"Unknown Attribute": "Attribut inconnu",
|
||||
"Unknown attribute selected": "Attribut inconnu sélectionné",
|
||||
"Condition Type": "Type d'état",
|
||||
"Required Value": "Valeur requise",
|
||||
"Selected Records": "Enregistrements sélectionnés",
|
||||
"No records added": "Aucun enregistrement ajouté",
|
||||
"Attribute": "Attribut",
|
||||
"Operator": "Opérateur",
|
||||
"Value": "Évaluer",
|
||||
"Delete this notification rule?": "Supprimer cette règle de notification ?",
|
||||
"New Notification Rule": "Nouvelle règle de notification",
|
||||
"Do you really want to delete this action?": "Voulez-vous vraiment supprimer cette action ?",
|
||||
"Add Action": "Ajouter une action",
|
||||
"These variables are available": "Ces variables sont disponibles",
|
||||
"Click or drag these in to the content area": "Cliquez ou faites-les glisser dans la zone de contenu",
|
||||
"This action does not provide any variables.": "Cette action ne fournit aucune variable.",
|
||||
"Delete Condition": "Supprimer la condition",
|
||||
"Add Condition": "Ajouter une condition",
|
||||
"Do you really want to delete this condition?": "Voulez-vous vraiment supprimer cette condition ?",
|
||||
"Please select a condition": "Veuillez sélectionner une condition"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Értesítés",
|
||||
"Notification Services": "Értesítési szolgáltatások",
|
||||
"Notification Rules": "Értesítési szabályok",
|
||||
"Manage the events and actions that trigger notifications.": "Az értesítéseket kiváltó események és műveletek kezelése.",
|
||||
"Name": "Név",
|
||||
"Code": "Kód",
|
||||
"Notification Rule": "Értesítési szabály",
|
||||
"Add Notification Rule": "Értesítési szabály hozzáadása",
|
||||
"Schedule": "Menetrend",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Vegye figyelembe, hogy az ütemezés bizonyos sor-illesztőprogram-konfigurációk esetén előfordulhat, hogy nem működik.",
|
||||
"Learn more": "Tudj meg többet",
|
||||
"The configured queue driver does not support delayed execution.": "A konfigurált sor-illesztőprogram nem támogatja a késleltetett végrehajtást.",
|
||||
"Notifications management": "Értesítések kezelése",
|
||||
"Return to Notifications": "Vissza az Értesítésekhez",
|
||||
"Are you sure?": "biztos vagy ebben?",
|
||||
"Action": "Akció",
|
||||
"Action Description": "Művelet leírása",
|
||||
"Compound Condition": "Összetett állapot",
|
||||
"ALL of subconditions should be": "MINDEN alfeltételnek meg kell lennie",
|
||||
"ANY of subconditions should be": "BÁRMILYEN alfeltételnek lennie kell",
|
||||
"ON": "TOVÁBB",
|
||||
"AND": "ÉS",
|
||||
"OR": "VAGY",
|
||||
"ALL subconditions should meet the requirement": "MINDEN alfeltételnek meg kell felelnie a követelménynek",
|
||||
"ANY subconditions should meet the requirement": "BÁRMELY alfeltételnek meg kell felelnie a követelménynek",
|
||||
"Condition Text": "Állapot szöveg",
|
||||
"Condition": "Feltétel",
|
||||
"Event": "Esemény",
|
||||
"Event description": "Esemény leírása",
|
||||
"is": "van",
|
||||
"is not": "nem",
|
||||
"equals or greater than": "egyenlő vagy nagyobb mint",
|
||||
"equals or less than": "egyenlő vagy kisebb mint",
|
||||
"contains": "tartalmaz",
|
||||
"does not contain": "nem tartalmaz",
|
||||
"greater than": "nagyobb, mint",
|
||||
"less than": "kevesebb, mint",
|
||||
"is one of": "egyike a",
|
||||
"is not one of": "nem egyike",
|
||||
"Unknown Attribute": "Ismeretlen tulajdonság",
|
||||
"Unknown attribute selected": "Ismeretlen attribútum kiválasztva",
|
||||
"Condition Type": "Állapot típusa",
|
||||
"Required Value": "Kötelező érték",
|
||||
"Selected Records": "Kiválasztott rekordok",
|
||||
"No records added": "Nincsenek hozzáadva rekordok",
|
||||
"Attribute": "Tulajdonság",
|
||||
"Operator": "Operátor",
|
||||
"Value": "Érték",
|
||||
"Delete this notification rule?": "Törli ezt az értesítési szabályt?",
|
||||
"New Notification Rule": "Új értesítési szabály",
|
||||
"Do you really want to delete this action?": "Biztosan törölni szeretné ezt a műveletet?",
|
||||
"Add Action": "Művelet hozzáadása",
|
||||
"These variables are available": "Ezek a változók elérhetők",
|
||||
"Click or drag these in to the content area": "Kattintson vagy húzza át ezeket a tartalomterületre",
|
||||
"This action does not provide any variables.": "Ez a művelet nem biztosít változókat.",
|
||||
"Delete Condition": "Feltétel törlése",
|
||||
"Add Condition": "Feltétel hozzáadása",
|
||||
"Do you really want to delete this condition?": "Valóban törölni szeretné ezt a feltételt?",
|
||||
"Please select a condition": "Kérjük, válasszon egy feltételt"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Notificare",
|
||||
"Notification Services": "Servizi di notifica",
|
||||
"Notification Rules": "Regole di notifica",
|
||||
"Manage the events and actions that trigger notifications.": "Gestisci gli eventi e le azioni che attivano le notifiche.",
|
||||
"Name": "Nome",
|
||||
"Code": "Codice",
|
||||
"Notification Rule": "Regola di notifica",
|
||||
"Add Notification Rule": "Aggiungi regola di notifica",
|
||||
"Schedule": "Programma",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Si noti che la pianificazione potrebbe non funzionare per alcune configurazioni del driver della coda.",
|
||||
"Learn more": "Per saperne di più",
|
||||
"The configured queue driver does not support delayed execution.": "Il driver della coda configurato non supporta l'esecuzione ritardata.",
|
||||
"Notifications management": "Gestione delle notifiche",
|
||||
"Return to Notifications": "Torna a Notifiche",
|
||||
"Are you sure?": "Sei sicuro?",
|
||||
"Action": "Azione",
|
||||
"Action Description": "Descrizione azione",
|
||||
"Compound Condition": "Condizione composta",
|
||||
"ALL of subconditions should be": "TUTTE le sottocondizioni dovrebbero esserlo",
|
||||
"ANY of subconditions should be": "QUALSIASI delle sottocondizioni dovrebbe essere",
|
||||
"ON": "SU",
|
||||
"AND": "E",
|
||||
"OR": "O",
|
||||
"ALL subconditions should meet the requirement": "TUTTE le sottocondizioni devono soddisfare il requisito",
|
||||
"ANY subconditions should meet the requirement": "QUALSIASI sottocondizione deve soddisfare il requisito",
|
||||
"Condition Text": "Testo delle condizioni",
|
||||
"Condition": "Condizione",
|
||||
"Event": "Evento",
|
||||
"Event description": "Descrizione dell'evento",
|
||||
"is": "è",
|
||||
"is not": "non è",
|
||||
"equals or greater than": "uguale o maggiore di",
|
||||
"equals or less than": "uguale o minore di",
|
||||
"contains": "contiene",
|
||||
"does not contain": "non contiene",
|
||||
"greater than": "più grande di",
|
||||
"less than": "meno di",
|
||||
"is one of": "è uno di",
|
||||
"is not one of": "non è uno di",
|
||||
"Unknown Attribute": "Attributo sconosciuto",
|
||||
"Unknown attribute selected": "Attributo sconosciuto selezionato",
|
||||
"Condition Type": "Tipo di condizione",
|
||||
"Required Value": "Valore richiesto",
|
||||
"Selected Records": "Record selezionati",
|
||||
"No records added": "Nessun record aggiunto",
|
||||
"Attribute": "Attributo",
|
||||
"Operator": "Operatore",
|
||||
"Value": "Valore",
|
||||
"Delete this notification rule?": "Eliminare questa regola di notifica?",
|
||||
"New Notification Rule": "Nuova regola di notifica",
|
||||
"Do you really want to delete this action?": "Vuoi davvero eliminare questa azione?",
|
||||
"Add Action": "Aggiungi azione",
|
||||
"These variables are available": "Queste variabili sono disponibili",
|
||||
"Click or drag these in to the content area": "Fare clic o trascinarli nell'area del contenuto",
|
||||
"This action does not provide any variables.": "Questa azione non fornisce alcuna variabile.",
|
||||
"Delete Condition": "Elimina condizione",
|
||||
"Add Condition": "Aggiungi condizione",
|
||||
"Do you really want to delete this condition?": "Vuoi davvero eliminare questa condizione?",
|
||||
"Please select a condition": "Seleziona una condizione"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Informeer",
|
||||
"Notification Services": "Meldingsservices",
|
||||
"Notification Rules": "Meldingsregels",
|
||||
"Manage the events and actions that trigger notifications.": "Beheer de gebeurtenissen en acties die meldingen activeren.",
|
||||
"Name": "Naam",
|
||||
"Code": "Code",
|
||||
"Notification Rule": "Meldingsregel",
|
||||
"Add Notification Rule": "Meldingsregel toevoegen",
|
||||
"Schedule": "Schema",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Houd er rekening mee dat planning mogelijk niet werkt voor bepaalde configuraties van wachtrijstuurprogramma's.",
|
||||
"Learn more": "Leer meer",
|
||||
"The configured queue driver does not support delayed execution.": "Het geconfigureerde wachtrijstuurprogramma ondersteunt geen vertraagde uitvoering.",
|
||||
"Notifications management": "Beheer van meldingen",
|
||||
"Return to Notifications": "Keer terug naar Meldingen",
|
||||
"Are you sure?": "Weet je het zeker?",
|
||||
"Action": "Actie",
|
||||
"Action Description": "Actiebeschrijving",
|
||||
"Compound Condition": "Samengestelde toestand:",
|
||||
"ALL of subconditions should be": "ALLE subvoorwaarden zouden moeten zijn:",
|
||||
"ANY of subconditions should be": "ELK van de subvoorwaarden zou moeten zijn:",
|
||||
"ON": "AAN",
|
||||
"AND": "EN",
|
||||
"OR": "OF",
|
||||
"ALL subconditions should meet the requirement": "ALLE subvoorwaarden moeten aan de eis voldoen",
|
||||
"ANY subconditions should meet the requirement": "ELKE subvoorwaarden zouden aan de eis moeten voldoen",
|
||||
"Condition Text": "Voorwaarde Tekst",
|
||||
"Condition": "Voorwaarde",
|
||||
"Event": "Evenement",
|
||||
"Event description": "Beschrijving van het evenement",
|
||||
"is": "is",
|
||||
"is not": "is niet",
|
||||
"equals or greater than": "gelijk aan of groter dan",
|
||||
"equals or less than": "gelijk aan of kleiner dan",
|
||||
"contains": "bevat",
|
||||
"does not contain": "bevat geen",
|
||||
"greater than": "groter dan",
|
||||
"less than": "minder dan",
|
||||
"is one of": "is een van",
|
||||
"is not one of": "is niet een van",
|
||||
"Unknown Attribute": "Onbekend kenmerk",
|
||||
"Unknown attribute selected": "Onbekend kenmerk geselecteerd",
|
||||
"Condition Type": "Conditietype:",
|
||||
"Required Value": "Vereiste waarde",
|
||||
"Selected Records": "Geselecteerde records",
|
||||
"No records added": "Geen records toegevoegd",
|
||||
"Attribute": "Attribuut",
|
||||
"Operator": "Operator",
|
||||
"Value": "Waarde",
|
||||
"Delete this notification rule?": "Deze meldingsregel verwijderen?",
|
||||
"New Notification Rule": "Nieuwe meldingsregel",
|
||||
"Do you really want to delete this action?": "Wil je deze actie echt verwijderen?",
|
||||
"Add Action": "Actie toevoegen",
|
||||
"These variables are available": "Deze variabelen zijn beschikbaar",
|
||||
"Click or drag these in to the content area": "Klik of sleep deze naar het inhoudsgebied",
|
||||
"This action does not provide any variables.": "Deze actie biedt geen variabelen.",
|
||||
"Delete Condition": "Voorwaarde verwijderen",
|
||||
"Add Condition": "Voorwaarde toevoegen",
|
||||
"Do you really want to delete this condition?": "Wilt u deze voorwaarde echt verwijderen?",
|
||||
"Please select a condition": "Selecteer een voorwaarde"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Уведомлять",
|
||||
"Notification Services": "Службы уведомлений",
|
||||
"Notification Rules": "Правила уведомления",
|
||||
"Manage the events and actions that trigger notifications.": "Управляйте событиями и действиями, вызывающими уведомления.",
|
||||
"Name": "Имя",
|
||||
"Code": "Код",
|
||||
"Notification Rule": "Правило уведомления",
|
||||
"Add Notification Rule": "Добавить правило уведомления",
|
||||
"Schedule": "Расписание",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Обратите внимание, что планирование может не работать для определенных конфигураций драйвера очереди.",
|
||||
"Learn more": "Узнать больше",
|
||||
"The configured queue driver does not support delayed execution.": "Настроенный драйвер очереди не поддерживает отложенное выполнение.",
|
||||
"Notifications management": "Управление уведомлениями",
|
||||
"Return to Notifications": "Вернуться к уведомлениям",
|
||||
"Are you sure?": "Вы уверены?",
|
||||
"Action": "Действие",
|
||||
"Action Description": "Описание действия",
|
||||
"Compound Condition": "Сложное состояние",
|
||||
"ALL of subconditions should be": "ВСЕ подусловия должны быть",
|
||||
"ANY of subconditions should be": "ЛЮБОЕ из подусловий должно быть",
|
||||
"ON": "НА",
|
||||
"AND": "А ТАКЖЕ",
|
||||
"OR": "ИЛИ",
|
||||
"ALL subconditions should meet the requirement": "ВСЕ подусловия должны соответствовать требованию",
|
||||
"ANY subconditions should meet the requirement": "ЛЮБЫЕ подусловия должны соответствовать требованию",
|
||||
"Condition Text": "Текст условия",
|
||||
"Condition": "Условие",
|
||||
"Event": "Мероприятие",
|
||||
"Event description": "Описание события",
|
||||
"is": "является",
|
||||
"is not": "не является",
|
||||
"equals or greater than": "равно или больше, чем",
|
||||
"equals or less than": "равно или меньше чем",
|
||||
"contains": "содержит",
|
||||
"does not contain": "не содержит",
|
||||
"greater than": "лучше чем",
|
||||
"less than": "меньше, чем",
|
||||
"is one of": "один из",
|
||||
"is not one of": "не является одним из",
|
||||
"Unknown Attribute": "Неизвестный атрибут",
|
||||
"Unknown attribute selected": "Выбран неизвестный атрибут",
|
||||
"Condition Type": "Тип условия",
|
||||
"Required Value": "Требуемое значение",
|
||||
"Selected Records": "Выбранные записи",
|
||||
"No records added": "Записи не добавлены",
|
||||
"Attribute": "Атрибут",
|
||||
"Operator": "Оператор",
|
||||
"Value": "Стоимость",
|
||||
"Delete this notification rule?": "Удалить это правило уведомлений?",
|
||||
"New Notification Rule": "Новое правило уведомления",
|
||||
"Do you really want to delete this action?": "Вы действительно хотите удалить это действие?",
|
||||
"Add Action": "Добавить действие",
|
||||
"These variables are available": "Эти переменные доступны",
|
||||
"Click or drag these in to the content area": "Нажмите или перетащите их в область содержимого.",
|
||||
"This action does not provide any variables.": "Это действие не предоставляет никаких переменных.",
|
||||
"Delete Condition": "Удалить условие",
|
||||
"Add Condition": "Добавить условие",
|
||||
"Do you really want to delete this condition?": "Вы действительно хотите удалить это условие?",
|
||||
"Please select a condition": "Пожалуйста, выберите условие"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Upozorniť",
|
||||
"Notification Services": "Notifikačné služby",
|
||||
"Notification Rules": "Pravidlá oznamovania",
|
||||
"Manage the events and actions that trigger notifications.": "Spravujte udalosti a akcie, ktoré spúšťajú upozornenia.",
|
||||
"Name": "názov",
|
||||
"Code": "kód",
|
||||
"Notification Rule": "Pravidlo oznamovania",
|
||||
"Add Notification Rule": "Pridať pravidlo upozornenia",
|
||||
"Schedule": "Rozvrh",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Upozorňujeme, že plánovanie nemusí fungovať pre určité konfigurácie ovládača frontu.",
|
||||
"Learn more": "Uč sa viac",
|
||||
"The configured queue driver does not support delayed execution.": "Nakonfigurovaný ovládač frontu nepodporuje oneskorené spustenie.",
|
||||
"Notifications management": "Správa upozornení",
|
||||
"Return to Notifications": "Vráťte sa k upozorneniam",
|
||||
"Are you sure?": "Si si istý?",
|
||||
"Action": "Akcia",
|
||||
"Action Description": "Popis akcie",
|
||||
"Compound Condition": "Zložený stav",
|
||||
"ALL of subconditions should be": "VŠETKY čiastkové podmienky by mali byť",
|
||||
"ANY of subconditions should be": "AKÉKOĽVEK z čiastkových podmienok by malo byť",
|
||||
"ON": "ON",
|
||||
"AND": "A",
|
||||
"OR": "ALEBO",
|
||||
"ALL subconditions should meet the requirement": "VŠETKY čiastkové podmienky by mali spĺňať požiadavku",
|
||||
"ANY subconditions should meet the requirement": "AKÉKOĽVEK čiastkové podmienky by mali spĺňať požiadavku",
|
||||
"Condition Text": "Podmienka Text",
|
||||
"Condition": "Podmienka",
|
||||
"Event": "Udalosť",
|
||||
"Event description": "Popis udalosti",
|
||||
"is": "je",
|
||||
"is not": "nie je",
|
||||
"equals or greater than": "sa rovná alebo je väčšia ako",
|
||||
"equals or less than": "rovná sa alebo je menšia ako",
|
||||
"contains": "obsahuje",
|
||||
"does not contain": "neobsahuje",
|
||||
"greater than": "väčší než",
|
||||
"less than": "menej ako",
|
||||
"is one of": "je jeden z",
|
||||
"is not one of": "nie je jedným z",
|
||||
"Unknown Attribute": "Neznámy atribút",
|
||||
"Unknown attribute selected": "Bol vybratý neznámy atribút",
|
||||
"Condition Type": "Typ stavu",
|
||||
"Required Value": "Požadovaná hodnota",
|
||||
"Selected Records": "Vybrané záznamy",
|
||||
"No records added": "Neboli pridané žiadne záznamy",
|
||||
"Attribute": "Atribút",
|
||||
"Operator": "Operátor",
|
||||
"Value": "Hodnota",
|
||||
"Delete this notification rule?": "Chcete odstrániť toto pravidlo upozornení?",
|
||||
"New Notification Rule": "Nové pravidlo oznamovania",
|
||||
"Do you really want to delete this action?": "Naozaj chcete odstrániť túto akciu?",
|
||||
"Add Action": "Pridať akciu",
|
||||
"These variables are available": "Tieto premenné sú k dispozícii",
|
||||
"Click or drag these in to the content area": "Kliknite alebo presuňte ich do oblasti obsahu",
|
||||
"This action does not provide any variables.": "Táto akcia neposkytuje žiadne premenné.",
|
||||
"Delete Condition": "Odstrániť podmienku",
|
||||
"Add Condition": "Pridať podmienku",
|
||||
"Do you really want to delete this condition?": "Naozaj chcete odstrániť túto podmienku?",
|
||||
"Please select a condition": "Vyberte podmienku"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Obvestiti",
|
||||
"Notification Services": "Storitve obveščanja",
|
||||
"Notification Rules": "Pravila obveščanja",
|
||||
"Manage the events and actions that trigger notifications.": "Upravljajte dogodke in dejanja, ki sprožijo obvestila.",
|
||||
"Name": "ime",
|
||||
"Code": "Koda",
|
||||
"Notification Rule": "Pravilo obveščanja",
|
||||
"Add Notification Rule": "Dodajte pravilo obveščanja",
|
||||
"Schedule": "Urnik",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Upoštevajte, da razporejanje morda ne bo delovalo za določene konfiguracije gonilnikov čakalne vrste.",
|
||||
"Learn more": "Nauči se več",
|
||||
"The configured queue driver does not support delayed execution.": "Konfiguriran gonilnik čakalne vrste ne podpira zakasnjenega izvajanja.",
|
||||
"Notifications management": "Upravljanje obvestil",
|
||||
"Return to Notifications": "Nazaj na Obvestila",
|
||||
"Are you sure?": "Ali si prepričan?",
|
||||
"Action": "Ukrep",
|
||||
"Action Description": "Opis dejanja",
|
||||
"Compound Condition": "Sestavljeno stanje",
|
||||
"ALL of subconditions should be": "VSI podpogoji bi morali biti",
|
||||
"ANY of subconditions should be": "KOT KOLI od podpogojev bi moral biti",
|
||||
"ON": "VKLOPLJENO",
|
||||
"AND": "IN",
|
||||
"OR": "ALI",
|
||||
"ALL subconditions should meet the requirement": "VSI podpogoji morajo izpolnjevati zahtevo",
|
||||
"ANY subconditions should meet the requirement": "VSAKI podpogoji bi morali izpolnjevati zahtevo",
|
||||
"Condition Text": "Besedilo pogoja",
|
||||
"Condition": "Stanje",
|
||||
"Event": "Dogodek",
|
||||
"Event description": "Opis dogodka",
|
||||
"is": "je",
|
||||
"is not": "ni",
|
||||
"equals or greater than": "enak ali večji od",
|
||||
"equals or less than": "enako ali manjše od",
|
||||
"contains": "vsebuje",
|
||||
"does not contain": "ne vsebuje",
|
||||
"greater than": "večji kot",
|
||||
"less than": "manj kot",
|
||||
"is one of": "je eden od",
|
||||
"is not one of": "ni eden od",
|
||||
"Unknown Attribute": "Neznan atribut",
|
||||
"Unknown attribute selected": "Neznan atribut je izbran",
|
||||
"Condition Type": "Vrsta pogoja",
|
||||
"Required Value": "Zahtevana vrednost",
|
||||
"Selected Records": "Izbrani zapisi",
|
||||
"No records added": "Dodan ni noben zapis",
|
||||
"Attribute": "atribut",
|
||||
"Operator": "Operater",
|
||||
"Value": "vrednost",
|
||||
"Delete this notification rule?": "Želite izbrisati to pravilo obveščanja?",
|
||||
"New Notification Rule": "Novo pravilo obveščanja",
|
||||
"Do you really want to delete this action?": "Ali res želite izbrisati to dejanje?",
|
||||
"Add Action": "Dodaj dejanje",
|
||||
"These variables are available": "Te spremenljivke so na voljo",
|
||||
"Click or drag these in to the content area": "Kliknite ali povlecite jih v območje vsebine",
|
||||
"This action does not provide any variables.": "To dejanje ne zagotavlja nobenih spremenljivk.",
|
||||
"Delete Condition": "Izbriši pogoj",
|
||||
"Add Condition": "Dodaj pogoj",
|
||||
"Do you really want to delete this condition?": "Ali res želite izbrisati ta pogoj?",
|
||||
"Please select a condition": "Prosimo, izberite pogoj"
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"Notify": "Notify",
|
||||
"Notification Services": "Notification Services",
|
||||
"Notification Rules": "Notification Rules",
|
||||
"Manage the events and actions that trigger notifications.": "Manage the events and actions that trigger notifications.",
|
||||
"Name": "Name",
|
||||
"Code": "Code",
|
||||
"Notification Rule": "Notification Rule",
|
||||
"Add Notification Rule": "Add Notification Rule",
|
||||
"Schedule": "Schedule",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "Note that scheduling might not work for certain queue driver configurations.",
|
||||
"Learn more": "Learn more",
|
||||
"The configured queue driver does not support delayed execution.": "The configured queue driver does not support delayed execution.",
|
||||
"Notifications management": "Notifications management",
|
||||
"Return to Notifications": "Return to Notifications",
|
||||
"Are you sure?": "Are you sure?",
|
||||
"Action": "Action",
|
||||
"Action Description": "Action Description",
|
||||
"Compound Condition": "Compound Condition",
|
||||
"ALL of subconditions should be": "ALL of subconditions should be",
|
||||
"ANY of subconditions should be": "ANY of subconditions should be",
|
||||
"ON": "ON",
|
||||
"AND": "AND",
|
||||
"OR": "OR",
|
||||
"ALL subconditions should meet the requirement": "ALL subconditions should meet the requirement",
|
||||
"ANY subconditions should meet the requirement": "ANY subconditions should meet the requirement",
|
||||
"Condition Text": "Condition Text",
|
||||
"Condition": "Condition",
|
||||
"Event": "Event",
|
||||
"Event description": "Event description",
|
||||
"is": "is",
|
||||
"is not": "is not",
|
||||
"equals or greater than": "equals or greater than",
|
||||
"equals or less than": "equals or less than",
|
||||
"contains": "contains",
|
||||
"does not contain": "does not contain",
|
||||
"greater than": "greater than",
|
||||
"less than": "less than",
|
||||
"is one of": "is one of",
|
||||
"is not one of": "is not one of",
|
||||
"Unknown Attribute": "Unknown Attribute",
|
||||
"Unknown attribute selected": "Unknown attribute selected",
|
||||
"Condition Type": "Condition Type",
|
||||
"Required Value": "Required Value",
|
||||
"Selected Records": "Selected Records",
|
||||
"No records added": "No records added",
|
||||
"Attribute": "Attribute",
|
||||
"Operator": "Operator",
|
||||
"Value": "Value",
|
||||
"Delete this notification rule?": "Delete this notification rule?",
|
||||
"New Notification Rule": "New Notification Rule",
|
||||
"Do you really want to delete this action?": "Do you really want to delete this action?",
|
||||
"Add Action": "Add Action",
|
||||
"These variables are available": "These variables are available",
|
||||
"Click or drag these in to the content area": "Click or drag these in to the content area",
|
||||
"This action does not provide any variables.": "This action does not provide any variables.",
|
||||
"Delete Condition": "Delete Condition",
|
||||
"Add Condition": "Add Condition",
|
||||
"Do you really want to delete this condition?": "Do you really want to delete this condition?",
|
||||
"Please select a condition": "Please select a condition"
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"Notify": "通知",
|
||||
"Notification Services": "通知服务",
|
||||
"Notification Rules": "通知规则",
|
||||
"Manage the events and actions that trigger notifications.": "管理触发通知的事件及动作",
|
||||
"Settings": "设置",
|
||||
"Actions": "操作",
|
||||
"Conditions": "条件",
|
||||
"Active": "活动",
|
||||
"Description": "描述",
|
||||
"API Code": "API 代码",
|
||||
"Compound condition": "复合条件",
|
||||
"Execution condition": "执行条件",
|
||||
"Application environment": "应用环境",
|
||||
"Request context": "请求上下文",
|
||||
"Active theme": "活动主题",
|
||||
"Visitor locale": "访客语言环境",
|
||||
"Name": "名称",
|
||||
"Code": "代码",
|
||||
"Notification Rule": "通知规则",
|
||||
"Add Notification Rule": "添加通知规则",
|
||||
"Schedule": "时间安排",
|
||||
"None": "无",
|
||||
"Delay execution": "延迟执行",
|
||||
"Seconds": "秒",
|
||||
"Minutes": "分",
|
||||
"Hours": "时",
|
||||
"Days": "天",
|
||||
"Months": "月",
|
||||
"Note that scheduling might not work for certain queue driver configurations.": "请注意,对于某些队列驱动程序配置,调度可能不起作用",
|
||||
"Learn more": "了解更多",
|
||||
"The configured queue driver does not support delayed execution.": "配置的队列驱动不支持延迟执行",
|
||||
"Notifications management": "通知管理",
|
||||
"Return to Notifications": "返回通知",
|
||||
"Are you sure?": "你确定吗?",
|
||||
"Action": "操作",
|
||||
"Action Description": "操作描述",
|
||||
"Compound Condition": "复合条件",
|
||||
"ALL of subconditions should be": "所有子条件都应该是",
|
||||
"ANY of subconditions should be": "任意一个子条件应该是",
|
||||
"ON": "在",
|
||||
"AND": "并且",
|
||||
"OR": "或者",
|
||||
"ALL subconditions should meet the requirement": "满足所有子条件",
|
||||
"ANY subconditions should meet the requirement": "满足任意一个子条件",
|
||||
"Condition Text": "条件文本",
|
||||
"Condition": "条件",
|
||||
"Event": "事件",
|
||||
"Event description": "事件描述",
|
||||
"is": "是",
|
||||
"is not": "不是",
|
||||
"equals or greater than": "等于或大于",
|
||||
"equals or less than": "等于或小于",
|
||||
"contains": "包含",
|
||||
"does not contain": "不包含",
|
||||
"greater than": "大于",
|
||||
"less than": "小于",
|
||||
"is one of": "是其中之一",
|
||||
"is not one of": "不是其中之一",
|
||||
"Unknown Attribute": "未知属性",
|
||||
"Unknown attribute selected": "选择了未知属性",
|
||||
"Condition Type": "条件类型",
|
||||
"Required Value": "必填值",
|
||||
"Selected Records": "选定记录",
|
||||
"No records added": "未选定记录",
|
||||
"Attribute": "属性",
|
||||
"Operator": "操作",
|
||||
"Value": "值",
|
||||
"Delete this notification rule?": "要删除此通知规则吗?",
|
||||
"New Notification Rule": "新增通知规则",
|
||||
"Do you really want to delete this action?": "您真的要删除此操作吗?",
|
||||
"Add Action": "添加操作",
|
||||
"These variables are available": "这些变量可用",
|
||||
"Click or drag these in to the content area": "单击或将它们拖入内容区域",
|
||||
"This action does not provide any variables.": "此操作不提供任何变量",
|
||||
"Delete Condition": "删除条件",
|
||||
"Add Condition": "添加条件",
|
||||
"Do you really want to delete this condition?": "您真的要删除此条件吗?",
|
||||
"Please select a condition": "请选择条件"
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
<?php namespace RainLab\Notify\Models;
|
||||
|
||||
use Model;
|
||||
use Markdown;
|
||||
|
||||
/**
|
||||
* Notification Model stored in the database
|
||||
*/
|
||||
class Notification extends Model
|
||||
{
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
public $table = 'rainlab_notify_notifications';
|
||||
|
||||
/**
|
||||
* @var array Guarded fields
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* @var array Fillable fields
|
||||
*/
|
||||
protected $fillable = [];
|
||||
|
||||
/**
|
||||
* @var array List of attribute names which are json encoded and decoded from the database.
|
||||
*/
|
||||
protected $jsonable = ['data'];
|
||||
|
||||
/**
|
||||
* @var array List of datetime attributes to convert to an instance of Carbon/DateTime objects.
|
||||
*/
|
||||
protected $dates = ['read_at'];
|
||||
|
||||
/**
|
||||
* The accessors to append to the model's array form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = ['parsed_body'];
|
||||
|
||||
/**
|
||||
* @var array Relations
|
||||
*/
|
||||
public $morphTo = [
|
||||
'notifiable' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Mark the notification as read.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function markAsRead()
|
||||
{
|
||||
if (is_null($this->read_at)) {
|
||||
$this->forceFill(['read_at' => $this->freshTimestamp()])->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a notification has been read.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function read()
|
||||
{
|
||||
return $this->read_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a notification has not been read.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unread()
|
||||
{
|
||||
return $this->read_at === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity's unread notifications.
|
||||
*/
|
||||
public function scopeApplyUnread($query)
|
||||
{
|
||||
return $query->whereNull('read_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity's read notifications.
|
||||
*/
|
||||
public function scopeApplyRead($query)
|
||||
{
|
||||
return $query->whereNotNull('read_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parsed body of the announcement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getParsedBodyAttribute()
|
||||
{
|
||||
return Markdown::parse($this->body);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
<?php namespace RainLab\Notify\Models;
|
||||
|
||||
use Lang;
|
||||
use Model;
|
||||
use RainLab\Notify\Classes\EventBase;
|
||||
use RainLab\Notify\Classes\ConditionBase;
|
||||
|
||||
/**
|
||||
* Notification rule
|
||||
*
|
||||
* @package rainlab\notify
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class NotificationRule extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Purgeable;
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
protected $table = 'rainlab_notify_notification_rules';
|
||||
|
||||
/**
|
||||
* @var array Guarded fields
|
||||
*/
|
||||
protected $guarded = ['config_data'];
|
||||
|
||||
/**
|
||||
* @var array Fillable fields
|
||||
*/
|
||||
protected $fillable = [];
|
||||
|
||||
/**
|
||||
* @var array List of attribute names which should not be saved to the database.
|
||||
*/
|
||||
protected $purgeable = ['event_name'];
|
||||
|
||||
/**
|
||||
* @var array The rules to be applied to the data.
|
||||
*/
|
||||
public $rules = [
|
||||
'name' => 'required'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Relations
|
||||
*/
|
||||
public $hasMany = [
|
||||
'rule_conditions' => [
|
||||
RuleCondition::class,
|
||||
'key' => 'rule_host_id',
|
||||
'conditions' => 'rule_parent_id is null',
|
||||
'delete' => true
|
||||
],
|
||||
'rule_actions' => [
|
||||
RuleAction::class,
|
||||
'key' => 'rule_host_id',
|
||||
'delete' => true
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Kicks off this notification rule, fires the event to obtain its parameters,
|
||||
* checks the rule conditions evaluate as true, then spins over each action.
|
||||
*/
|
||||
public function triggerRule()
|
||||
{
|
||||
$params = $this->getEventObject()->getParams();
|
||||
$rootCondition = $this->rule_conditions->first();
|
||||
|
||||
if ($rootCondition && !$rootCondition->getConditionObject()->isTrue($params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->rule_actions as $action) {
|
||||
$action->setRelation('notification_rule', $this);
|
||||
$action->triggerAction($params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extra conditions provided by the event.
|
||||
* @return array
|
||||
*/
|
||||
public function getExtraConditionRules()
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
$classes = $this->getEventObject()->defineConditions();
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$rules[$class] = new $class;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends this class with the event class
|
||||
* @param string $class Class name
|
||||
* @return boolean
|
||||
*/
|
||||
public function applyEventClass($class = null)
|
||||
{
|
||||
if (!$class) {
|
||||
$class = $this->class_name;
|
||||
}
|
||||
|
||||
if (!$class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isClassExtendedWith($class)) {
|
||||
$this->extendClassWith($class);
|
||||
}
|
||||
|
||||
$this->class_name = $class;
|
||||
$this->event_name = Lang::get(array_get($this->eventDetails(), 'name', 'Unknown'));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event class extension object.
|
||||
* @return \RainLab\Notify\Classes\NotificationEvent
|
||||
*/
|
||||
public function getEventObject()
|
||||
{
|
||||
$this->applyEventClass();
|
||||
|
||||
return $this->asExtension($this->getEventClass());
|
||||
}
|
||||
|
||||
public function getEventClass()
|
||||
{
|
||||
return $this->class_name;
|
||||
}
|
||||
|
||||
//
|
||||
// Events
|
||||
//
|
||||
|
||||
public function afterFetch()
|
||||
{
|
||||
$this->applyEventClass();
|
||||
}
|
||||
|
||||
public function beforeValidate()
|
||||
{
|
||||
if (!$this->applyEventClass()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Scopes
|
||||
//
|
||||
|
||||
public function scopeApplyEnabled($query)
|
||||
{
|
||||
return $query->where('is_enabled', true);
|
||||
}
|
||||
|
||||
public function scopeApplyClass($query, $class)
|
||||
{
|
||||
if (!is_string($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
|
||||
return $query->where('class_name', $class);
|
||||
}
|
||||
|
||||
//
|
||||
// Presets
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns an array of rule codes and descriptions.
|
||||
* @return array
|
||||
*/
|
||||
public static function listRulesForEvent($eventClass)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$dbRules = self::applyClass($eventClass)->get();
|
||||
$presets = (array) EventBase::findEventPresetsByClass($eventClass);
|
||||
|
||||
foreach ($dbRules as $dbRule) {
|
||||
if ($dbRule->code) {
|
||||
unset($presets[$dbRule->code]);
|
||||
}
|
||||
|
||||
if ($dbRule->is_enabled) {
|
||||
$results[] = $dbRule;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($presets as $code => $preset) {
|
||||
if ($newPreset = self::createFromPreset($code, $preset)) {
|
||||
$results[] = $newPreset;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncronise all file-based presets to the database.
|
||||
* @return void
|
||||
*/
|
||||
public static function syncAll()
|
||||
{
|
||||
$presets = (array) EventBase::findEventPresets();
|
||||
$dbRules = self::where('code', '!=', '')->whereNotNull('code')->lists('is_custom', 'code');
|
||||
$newRules = array_diff_key($presets, $dbRules);
|
||||
|
||||
/*
|
||||
* Clean up non-customized templates
|
||||
*/
|
||||
foreach ($dbRules as $code => $isCustom) {
|
||||
if ($isCustom) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!array_key_exists($code, $presets) && ($record = self::whereCode($code)->first())) {
|
||||
$record->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new rules
|
||||
*/
|
||||
foreach ($newRules as $code => $preset) {
|
||||
self::createFromPreset($code, $preset);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createFromPreset($code, $preset)
|
||||
{
|
||||
$actions = array_get($preset, 'items');
|
||||
if (!$actions || !is_array($actions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newRule = new self;
|
||||
$newRule->code = $code;
|
||||
$newRule->is_enabled = 1;
|
||||
$newRule->is_custom = 0;
|
||||
$newRule->name = array_get($preset, 'name');
|
||||
$newRule->class_name = array_get($preset, 'event');
|
||||
$newRule->forceSave();
|
||||
|
||||
// Add the actions
|
||||
foreach ($actions as $action) {
|
||||
$params = array_except($action, 'action');
|
||||
|
||||
$newAction = new RuleAction;
|
||||
$newAction->class_name = array_get($action, 'action');
|
||||
$newAction->notification_rule = $newRule;
|
||||
$newAction->fill($params);
|
||||
$newAction->forceSave();
|
||||
}
|
||||
|
||||
// Add the conditions
|
||||
$conditions = array_get($preset, 'conditions');
|
||||
if (!$conditions || !is_array($conditions)) {
|
||||
return $newRule;
|
||||
}
|
||||
|
||||
// Create the root condition
|
||||
$rootCondition = new RuleCondition();
|
||||
$rootCondition->rule_host_type = ConditionBase::TYPE_ANY;
|
||||
$rootCondition->class_name = $rootCondition->getRootConditionClass();
|
||||
$rootCondition->notification_rule = $newRule;
|
||||
$rootCondition->save();
|
||||
|
||||
// Add the sub conditions
|
||||
foreach ($conditions as $condition) {
|
||||
$params = array_except($condition, 'condition');
|
||||
$newCondition = new RuleCondition();
|
||||
$newCondition->class_name = array_get($condition, 'condition');
|
||||
$newCondition->parent = $rootCondition;
|
||||
$newCondition->fill($params);
|
||||
$newCondition->forceSave();
|
||||
}
|
||||
|
||||
return $newRule;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
<?php namespace Rainlab\Notify\Models;
|
||||
|
||||
use Model;
|
||||
use Exception;
|
||||
use Queue;
|
||||
use SystemException;
|
||||
use Rainlab\Notify\Classes\ScheduledAction;
|
||||
|
||||
/**
|
||||
* RuleAction Model
|
||||
*/
|
||||
class RuleAction extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
public $table = 'rainlab_notify_rule_actions';
|
||||
|
||||
/**
|
||||
* @var array Guarded fields
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* @var array Fillable fields
|
||||
*/
|
||||
protected $fillable = [];
|
||||
|
||||
/**
|
||||
* @var array The rules to be applied to the data.
|
||||
*/
|
||||
public $rules = [];
|
||||
|
||||
/**
|
||||
* @var array List of attribute names which are json encoded and decoded from the database.
|
||||
*/
|
||||
protected $jsonable = ['config_data'];
|
||||
|
||||
/**
|
||||
* @var array Relations
|
||||
*/
|
||||
public $belongsTo = [
|
||||
'notification_rule' => [NotificationRule::class, 'key' => 'rule_host_id'],
|
||||
];
|
||||
|
||||
public function triggerAction($params, $scheduled = true)
|
||||
{
|
||||
try {
|
||||
$actionObject = $this->getActionObject();
|
||||
|
||||
// Check if action is muted in which case we don't proceed sending a notification
|
||||
if (method_exists($actionObject, 'isMuted') && $actionObject->isMuted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply action schedule
|
||||
if ($scheduled && $schedule = $this->getSchedule()) {
|
||||
// We delay the execution using Queues. When dequeued
|
||||
// ScheduledAction will call this triggerAction
|
||||
// function with $scheduled=false
|
||||
Queue::later($schedule, new ScheduledAction($this, $params));
|
||||
}
|
||||
else {
|
||||
// We trigger the action
|
||||
$actionObject->triggerAction($params);
|
||||
}
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
// We could log the error here, for now we should suppress
|
||||
// any exceptions to let other actions proceed as normal
|
||||
traceLog('Error with ' . $this->getActionClass());
|
||||
traceLog($ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends this model with the action class
|
||||
* @param string $class Class name
|
||||
* @return boolean
|
||||
*/
|
||||
public function applyActionClass($class = null)
|
||||
{
|
||||
if (!$class) {
|
||||
$class = $this->class_name;
|
||||
}
|
||||
|
||||
if (!$class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isClassExtendedWith($class)) {
|
||||
$this->extendClassWith($class);
|
||||
}
|
||||
|
||||
$this->class_name = $class;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforeSave()
|
||||
{
|
||||
$this->setCustomData();
|
||||
}
|
||||
|
||||
public function afterSave()
|
||||
{
|
||||
// Make sure that this record is removed from the DB after being removed from a rule
|
||||
$removedFromRule = $this->rule_host_id === null && $this->getOriginal('rule_host_id');
|
||||
if ($removedFromRule && !$this->notification_rule()->withDeferred(post('_session_key'))->exists()) {
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function applyCustomData()
|
||||
{
|
||||
$this->setCustomData();
|
||||
$this->loadCustomData();
|
||||
}
|
||||
|
||||
protected function loadCustomData()
|
||||
{
|
||||
$this->setRawAttributes((array) $this->getAttributes() + (array) $this->config_data, true);
|
||||
}
|
||||
|
||||
protected function setCustomData()
|
||||
{
|
||||
if (!$actionObj = $this->getActionObject()) {
|
||||
throw new SystemException(sprintf('Unable to find action object [%s]', $this->getActionClass()));
|
||||
}
|
||||
|
||||
/*
|
||||
* Spin over each field and add it to config_data
|
||||
*/
|
||||
|
||||
$metaAttributes = [
|
||||
'action_text',
|
||||
'schedule_type',
|
||||
'schedule_delay',
|
||||
'schedule_delay_factor',
|
||||
];
|
||||
|
||||
$formAttributes = [];
|
||||
$config = $actionObj->getFieldConfig();
|
||||
if (isset($config->fields)) {
|
||||
$formAttributes = array_keys($config->fields);
|
||||
}
|
||||
|
||||
$fieldAttributes = array_merge($formAttributes, $metaAttributes);
|
||||
|
||||
$dynamicAttributes = array_only($this->getAttributes(), $fieldAttributes);
|
||||
$this->config_data = $dynamicAttributes;
|
||||
|
||||
$this->setRawAttributes(array_except($this->getAttributes(), $fieldAttributes));
|
||||
}
|
||||
|
||||
public function afterFetch()
|
||||
{
|
||||
$this->applyActionClass();
|
||||
$this->loadCustomData();
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
if (strlen($this->action_text)) {
|
||||
return $this->action_text;
|
||||
}
|
||||
|
||||
if ($actionObj = $this->getActionObject()) {
|
||||
return $actionObj->getText();
|
||||
}
|
||||
}
|
||||
|
||||
public function getSchedule()
|
||||
{
|
||||
if ($this->schedule_type !== 'delayed') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_numeric($this->schedule_delay) || !is_numeric($this->schedule_delay_factor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$delay = (int) $this->schedule_delay;
|
||||
$delay_factor = (int) $this->schedule_delay_factor;
|
||||
|
||||
return abs($delay * $delay_factor);
|
||||
}
|
||||
|
||||
public function getActionObject()
|
||||
{
|
||||
$this->applyActionClass();
|
||||
|
||||
return $this->asExtension($this->getActionClass());
|
||||
}
|
||||
|
||||
public function getActionClass()
|
||||
{
|
||||
return $this->class_name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<?php namespace RainLab\Notify\Models;
|
||||
|
||||
use Model;
|
||||
use RainLab\Notify\Classes\CompoundCondition;
|
||||
use RainLab\Notify\Interfaces\CompoundCondition as CompoundConditionInterface;
|
||||
use SystemException;
|
||||
|
||||
/**
|
||||
* ConditionRule Model
|
||||
*/
|
||||
class RuleCondition extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
public $table = 'rainlab_notify_rule_conditions';
|
||||
|
||||
/**
|
||||
* @var array Guarded fields
|
||||
*/
|
||||
protected $guarded = [];
|
||||
/**
|
||||
* @var array Fillable fields
|
||||
*/
|
||||
protected $fillable = [];
|
||||
|
||||
/**
|
||||
* @var array The rules to be applied to the data.
|
||||
*/
|
||||
public $rules = [];
|
||||
|
||||
/**
|
||||
* @var array List of attribute names which are json encoded and decoded from the database.
|
||||
*/
|
||||
protected $jsonable = ['config_data'];
|
||||
|
||||
/**
|
||||
* @var array Relations
|
||||
*/
|
||||
public $hasMany = [
|
||||
'children' => [self::class, 'key' => 'rule_parent_id', 'delete' => true],
|
||||
];
|
||||
|
||||
public $belongsTo = [
|
||||
'parent' => [self::class, 'key' => 'rule_parent_id'],
|
||||
'notification_rule' => [NotificationRule::class, 'key'=>'rule_host_id']
|
||||
];
|
||||
|
||||
public function filterFields($fields, $context)
|
||||
{
|
||||
/*
|
||||
* Let the condition contribute
|
||||
*/
|
||||
$this->getConditionObject()->setFormFields($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends this model with the condition class
|
||||
* @param string $class Class name
|
||||
* @return boolean
|
||||
*/
|
||||
public function applyConditionClass($class = null)
|
||||
{
|
||||
if (!$class) {
|
||||
$class = $this->class_name;
|
||||
}
|
||||
|
||||
if (!$class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isClassExtendedWith($class)) {
|
||||
$this->extendClassWith($class);
|
||||
}
|
||||
|
||||
$this->class_name = $class;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforeSave()
|
||||
{
|
||||
$this->setCustomData();
|
||||
}
|
||||
|
||||
public function applyCustomData()
|
||||
{
|
||||
$this->setCustomData();
|
||||
$this->loadCustomData();
|
||||
}
|
||||
|
||||
protected function loadCustomData()
|
||||
{
|
||||
$this->setRawAttributes((array) $this->getAttributes() + (array) $this->config_data, true);
|
||||
}
|
||||
|
||||
protected function setCustomData()
|
||||
{
|
||||
/*
|
||||
* Let the condition contribute
|
||||
*/
|
||||
$this->getConditionObject()->setCustomData($this);
|
||||
|
||||
/*
|
||||
* Spin over each field and add it to config_data
|
||||
*/
|
||||
$config = $this->getFieldConfig();
|
||||
|
||||
if (!isset($config->fields)) {
|
||||
throw new SystemException('Condition class has no fields.');
|
||||
}
|
||||
|
||||
$staticAttributes = ['condition_text'];
|
||||
|
||||
$fieldAttributes = array_merge($staticAttributes, array_keys($config->fields));
|
||||
|
||||
$dynamicAttributes = array_only($this->getAttributes(), $fieldAttributes);
|
||||
|
||||
$this->config_data = $dynamicAttributes;
|
||||
|
||||
$this->setRawAttributes(array_except($this->getAttributes(), $fieldAttributes));
|
||||
}
|
||||
|
||||
public function afterFetch()
|
||||
{
|
||||
$this->applyConditionClass();
|
||||
$this->loadCustomData();
|
||||
}
|
||||
|
||||
public function afterSave()
|
||||
{
|
||||
// Make sure that this record is removed from the DB after being removed from a rule
|
||||
$removedFromRule = $this->rule_parent_id === null && $this->getOriginal('rule_parent_id');
|
||||
if ($removedFromRule && !$this->notification_rule()->withDeferred(post('_session_key'))->exists()) {
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
if (strlen($this->condition_text)) {
|
||||
return $this->condition_text;
|
||||
}
|
||||
|
||||
if ($conditionObj = $this->getConditionObject()) {
|
||||
return $conditionObj->getText();
|
||||
}
|
||||
}
|
||||
|
||||
public function isCompound()
|
||||
{
|
||||
return $this->getConditionObject() instanceof CompoundConditionInterface;
|
||||
}
|
||||
|
||||
public function getConditionObject()
|
||||
{
|
||||
$this->applyConditionClass();
|
||||
|
||||
return $this->asExtension($this->getConditionClass());
|
||||
}
|
||||
|
||||
public function getConditionClass()
|
||||
{
|
||||
return $this->class_name;
|
||||
}
|
||||
|
||||
public function getRootConditionClass()
|
||||
{
|
||||
return CompoundCondition::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# ===================================
|
||||
# Column Definitions
|
||||
# ===================================
|
||||
|
||||
columns:
|
||||
|
||||
name:
|
||||
label: Name
|
||||
searchable: true
|
||||
|
||||
code:
|
||||
label: Code
|
||||
searchable: true
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
name:
|
||||
label: Name
|
||||
placeholder: New notification rule name
|
||||
attributes:
|
||||
autofocus: 1
|
||||
|
||||
toolbar:
|
||||
type: partial
|
||||
path: form_toolbar
|
||||
cssClass: collapse-visible
|
||||
|
||||
tabs:
|
||||
stretch: true
|
||||
cssClass: master-area
|
||||
paneCssClass:
|
||||
'Actions': 'pane-compact'
|
||||
|
||||
fields:
|
||||
rule_actions:
|
||||
type: RainLab\Notify\FormWidgets\ActionBuilder
|
||||
tab: Actions
|
||||
|
||||
rule_conditions:
|
||||
type: RainLab\Notify\FormWidgets\ConditionBuilder
|
||||
tab: Conditions
|
||||
|
||||
is_enabled:
|
||||
label: Active
|
||||
type: checkbox
|
||||
tab: Settings
|
||||
default: true
|
||||
|
||||
description:
|
||||
label: Description
|
||||
type: textarea
|
||||
size: tiny
|
||||
tab: Settings
|
||||
|
||||
code:
|
||||
label: API Code
|
||||
span: auto
|
||||
tab: Settings
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# ===================================
|
||||
# List Column Definitions
|
||||
# ===================================
|
||||
|
||||
columns:
|
||||
id:
|
||||
label: ID
|
||||
searchable: true
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# ===================================
|
||||
# Form Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
id:
|
||||
label: ID
|
||||
disabled: true
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# ===================================
|
||||
# Form Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
schedule_type:
|
||||
type: dropdown
|
||||
options:
|
||||
disabled: None
|
||||
delayed: Delay execution
|
||||
schedule_delay:
|
||||
type: number
|
||||
span: left
|
||||
trigger:
|
||||
action: show
|
||||
field: schedule_type
|
||||
condition: value[delayed]
|
||||
schedule_delay_factor:
|
||||
type: dropdown
|
||||
span: right
|
||||
options:
|
||||
1: Seconds
|
||||
60: Minutes
|
||||
3600: Hours
|
||||
86400: Days
|
||||
2592000: Months
|
||||
trigger:
|
||||
action: show
|
||||
field: schedule_type
|
||||
condition: value[delayed]
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
<?php namespace RainLab\Notify\NotifyRules;
|
||||
|
||||
use Cms\Classes\Theme;
|
||||
use RainLab\Notify\Classes\ConditionBase;
|
||||
|
||||
class ExecutionContextCondition extends ConditionBase
|
||||
{
|
||||
protected $operators = [
|
||||
'is' => 'is',
|
||||
'is_not' => 'is not',
|
||||
];
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'Event is triggered from environment';
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Execution context';
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
$host = $this->host;
|
||||
$value = $host->value;
|
||||
$attribute = $host->subcondition;
|
||||
$subconditions = $this->getSubconditionOptions();
|
||||
|
||||
$result = array_get($subconditions, $attribute, 'Execution context');
|
||||
$result .= ' <span class="operator">'.array_get($this->operators, $host->operator, $host->operator).'</span> ';
|
||||
|
||||
if ($attribute == 'locale' || $attribute == 'environment') {
|
||||
$result .= strtolower($value) ?: '?';
|
||||
}
|
||||
elseif ($value) {
|
||||
$options = $this->getValueOptions();
|
||||
$result .= strtolower(array_get($options, $value));
|
||||
}
|
||||
else {
|
||||
$result .= '?';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a title to use for grouping subconditions
|
||||
* in the Create Condition drop-down menu
|
||||
*/
|
||||
public function getGroupingTitle()
|
||||
{
|
||||
return 'Execution context';
|
||||
}
|
||||
|
||||
public function listSubconditions()
|
||||
{
|
||||
return array_flip($this->getSubconditionOptions());
|
||||
}
|
||||
|
||||
public function initConfigData($host)
|
||||
{
|
||||
$host->operator = 'is';
|
||||
}
|
||||
|
||||
public function setFormFields($fields)
|
||||
{
|
||||
$attribute = $fields->subcondition->value;
|
||||
|
||||
if ($attribute == 'locale' || $attribute == 'environment') {
|
||||
$fields->value->type = 'text';
|
||||
}
|
||||
else {
|
||||
$fields->value->type = 'dropdown';
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueOptions()
|
||||
{
|
||||
$attribute = $this->host->subcondition;
|
||||
$result = [];
|
||||
|
||||
if ($attribute == 'context') {
|
||||
$result = [
|
||||
'backend' => 'Back-end area',
|
||||
'front' => 'Front-end website',
|
||||
'console' => 'Command line interface',
|
||||
];
|
||||
}
|
||||
|
||||
if ($attribute == 'theme') {
|
||||
foreach (Theme::all() as $theme) {
|
||||
$result[$theme->getDirName()] = $theme->getDirName();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getSubconditionOptions()
|
||||
{
|
||||
return [
|
||||
'environment' => 'Application environment',
|
||||
'context' => 'Request context',
|
||||
'theme' => 'Active theme',
|
||||
'locale' => 'Visitor locale',
|
||||
];
|
||||
}
|
||||
|
||||
public function getOperatorOptions()
|
||||
{
|
||||
return $this->operators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the condition is TRUE for specified parameters
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(&$params)
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
$attribute = $hostObj->subcondition;
|
||||
|
||||
$conditionValue = $hostObj->value;
|
||||
$conditionValue = trim(mb_strtolower($conditionValue));
|
||||
|
||||
if ($attribute == 'locale') {
|
||||
return array_get($params, 'appLocale') == $conditionValue;
|
||||
} else if ($attribute === 'environment') {
|
||||
return $conditionValue === \App::environment();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<?php namespace RainLab\Notify\NotifyRules;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use RainLab\Notify\Classes\ActionBase;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use ApplicationException;
|
||||
|
||||
class SaveDatabaseAction extends ActionBase
|
||||
{
|
||||
protected $tableDefinitions = [];
|
||||
|
||||
/**
|
||||
* Returns information about this event, including name and description.
|
||||
*/
|
||||
public function actionDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Store in database',
|
||||
'description' => 'Log event data in the notifications activity log',
|
||||
'icon' => 'icon-database'
|
||||
];
|
||||
}
|
||||
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
if ($this->host->related_object) {
|
||||
$label = array_get($this->getRelatedObjectOptions(), $this->host->related_object);
|
||||
|
||||
return 'Log event in the '.$label.' log';
|
||||
}
|
||||
|
||||
return parent::getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers this action.
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function triggerAction($params)
|
||||
{
|
||||
if (
|
||||
(!$definition = array_get($this->tableDefinitions, $this->host->related_object)) ||
|
||||
(!$param = array_get($definition, 'param')) ||
|
||||
(!$value = array_get($params, $param))
|
||||
) {
|
||||
throw new ApplicationException('Error evaluating the save database action: the related object is not found in the action parameters.');
|
||||
}
|
||||
|
||||
if (!$value instanceof EloquentModel) {
|
||||
// @todo Perhaps value is an ID or a model array,
|
||||
// look up model $definition[class] from ID ...
|
||||
}
|
||||
|
||||
$rule = $this->host->notification_rule;
|
||||
$relation = array_get($definition, 'relation');
|
||||
|
||||
$value->$relation()->create([
|
||||
'id' => Uuid::uuid4()->toString(),
|
||||
'event_type' => $rule->getEventClass(),
|
||||
'icon' => $this->host->icon,
|
||||
'type' => $this->host->type,
|
||||
'body' => $this->host->body,
|
||||
'data' => $this->getData($params),
|
||||
'read_at' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data for the notification.
|
||||
*
|
||||
* @param array $notifiable
|
||||
* @return array
|
||||
*/
|
||||
protected function getData($params)
|
||||
{
|
||||
// This should check for params that cannot be jsonable.
|
||||
return $params;
|
||||
}
|
||||
|
||||
public function getRelatedObjectOptions()
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->tableDefinitions as $key => $definition) {
|
||||
$result[$key] = array_get($definition, 'label');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getTableDefinitions()
|
||||
{
|
||||
return $this->tableDefinitions;
|
||||
}
|
||||
|
||||
public function addTableDefinition($options)
|
||||
{
|
||||
if (!$className = array_get($options, 'class')) {
|
||||
throw new ApplicationException('Missing class name from table definition.');
|
||||
}
|
||||
|
||||
$options = array_merge([
|
||||
'label' => 'Undefined table',
|
||||
'class' => null,
|
||||
'param' => null,
|
||||
'relation' => 'notifications',
|
||||
], $options);
|
||||
|
||||
$keyName = $className . '@' . array_get($options, 'relation');
|
||||
|
||||
$this->tableDefinitions[$keyName] = $options;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
<?php namespace RainLab\Notify\NotifyRules;
|
||||
|
||||
use Mail;
|
||||
use Lang;
|
||||
use Config;
|
||||
use System\Models\MailTemplate;
|
||||
use RainLab\Notify\Classes\ActionBase;
|
||||
use Backend\Models\User as AdminUserModel;
|
||||
use Backend\Models\UserGroup as AdminGroupModel;
|
||||
use ApplicationException;
|
||||
|
||||
class SendMailTemplateAction extends ActionBase
|
||||
{
|
||||
public $recipientModes = [
|
||||
'system' => 'System default',
|
||||
'user' => 'User email address (if applicable)',
|
||||
'sender' => 'Sender user email address (if applicable)',
|
||||
'admin' => 'Back-end administrators',
|
||||
'custom' => 'Specific email address',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns information about this event, including name and description.
|
||||
*/
|
||||
public function actionDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Compose a mail message',
|
||||
'description' => 'Send a message to a recipient',
|
||||
'icon' => 'icon-envelope'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers this action.
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function triggerAction($params)
|
||||
{
|
||||
$template = $this->host->mail_template;
|
||||
|
||||
$recipient = $this->getRecipientAddress($params);
|
||||
|
||||
$replyTo = $this->getReplyToAddress($params);
|
||||
|
||||
if (!$recipient || !$template) {
|
||||
throw new ApplicationException('Missing valid recipient or mail template');
|
||||
}
|
||||
|
||||
Mail::sendTo($recipient, $template, $params, function($message) use ($replyTo) {
|
||||
if ($replyTo) {
|
||||
$message->replyTo($replyTo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Field configuration for the action.
|
||||
*/
|
||||
public function defineFormFields()
|
||||
{
|
||||
return 'fields.yaml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines validation rules for the custom fields.
|
||||
* @return array
|
||||
*/
|
||||
public function defineValidationRules()
|
||||
{
|
||||
return [
|
||||
'mail_template' => 'required',
|
||||
'send_to_mode' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
if ($this->isAdminMode()) {
|
||||
return 'Compose mail to administrators';
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
}
|
||||
|
||||
public function getActionIcon()
|
||||
{
|
||||
if ($this->isAdminMode()) {
|
||||
return 'icon-envelope-square';
|
||||
}
|
||||
|
||||
return parent::getActionIcon();
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
$hostObj = $this->host;
|
||||
|
||||
$recipient = array_get($this->recipientModes, $hostObj->send_to_mode);
|
||||
|
||||
if ($this->isAdminMode()) {
|
||||
if ($groupId = $this->host->send_to_admin) {
|
||||
if ($group = AdminGroupModel::find($groupId)) {
|
||||
$adminText = $group->name;
|
||||
}
|
||||
else {
|
||||
$adminText = '?';
|
||||
}
|
||||
|
||||
$adminText .= ' admin group';
|
||||
}
|
||||
else {
|
||||
$adminText = 'all admins';
|
||||
}
|
||||
return sprintf(
|
||||
'Send a message to %s using template %s',
|
||||
$adminText,
|
||||
$hostObj->mail_template
|
||||
);
|
||||
}
|
||||
|
||||
if ($hostObj->mail_template) {
|
||||
return sprintf(
|
||||
'Send a message to %s using template %s',
|
||||
mb_strtolower($recipient),
|
||||
$hostObj->mail_template
|
||||
);
|
||||
}
|
||||
|
||||
return parent::getText();
|
||||
}
|
||||
|
||||
public function getSendToAdminOptions()
|
||||
{
|
||||
$options = ['' => '- All administrators -'];
|
||||
|
||||
$groups = AdminGroupModel::lists('name', 'id');
|
||||
|
||||
return $options + $groups;
|
||||
}
|
||||
|
||||
public function getSendToModeOptions()
|
||||
{
|
||||
$modes = $this->recipientModes;
|
||||
|
||||
unset($modes['system']);
|
||||
|
||||
return $modes;
|
||||
}
|
||||
|
||||
public function getReplyToModeOptions()
|
||||
{
|
||||
$modes = $this->recipientModes;
|
||||
|
||||
unset($modes['admin']);
|
||||
|
||||
return $modes;
|
||||
}
|
||||
|
||||
public function getMailTemplateOptions()
|
||||
{
|
||||
$codes = array_keys(MailTemplate::listAllTemplates());
|
||||
|
||||
$result = array_combine($codes, $codes);
|
||||
|
||||
// Wrap to prevent collisions with language keys
|
||||
array_walk($result, function(&$value, $key) {
|
||||
$value = "[$value]";
|
||||
});
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getReplyToAddress($params)
|
||||
{
|
||||
$mode = $this->host->reply_to_mode;
|
||||
|
||||
if ($mode == 'custom') {
|
||||
return $this->host->reply_to_custom;
|
||||
}
|
||||
|
||||
if ($mode == 'user' || $mode == 'sender') {
|
||||
$obj = array_get($params, $mode);
|
||||
return $obj->email;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRecipientAddress($params)
|
||||
{
|
||||
$mode = $this->host->send_to_mode;
|
||||
|
||||
if ($mode == 'custom') {
|
||||
return $this->host->send_to_custom;
|
||||
}
|
||||
|
||||
if ($mode == 'system') {
|
||||
$name = Config::get('mail.from.name', 'Your Site');
|
||||
$address = Config::get('mail.from.address', 'admin@domain.tld');
|
||||
return [$address => $name];
|
||||
}
|
||||
|
||||
if ($mode == 'admin') {
|
||||
if ($groupId = $this->host->send_to_admin) {
|
||||
if (!$group = AdminGroupModel::find($groupId)) {
|
||||
throw new ApplicationException('Unable to find admin group with ID: '.$groupId);
|
||||
}
|
||||
|
||||
return $group->users->lists('full_name', 'email');
|
||||
}
|
||||
else {
|
||||
return AdminUserModel::all()->lists('full_name', 'email');
|
||||
}
|
||||
}
|
||||
|
||||
if ($mode == 'user' || $mode == 'sender') {
|
||||
return array_get($params, $mode);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isAdminMode()
|
||||
{
|
||||
return $this->host->send_to_mode == 'admin';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
subcondition:
|
||||
label: Attribute
|
||||
span: auto
|
||||
type: dropdown
|
||||
|
||||
operator:
|
||||
label: Operator
|
||||
span: auto
|
||||
type: dropdown
|
||||
dependsOn: subcondition
|
||||
|
||||
value:
|
||||
label: Value
|
||||
dependsOn: [subcondition, operator]
|
||||
type: dropdown
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
related_object:
|
||||
label: Related object
|
||||
type: dropdown
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
mail_template:
|
||||
label: Mail template
|
||||
type: dropdown
|
||||
placeholder: Select template
|
||||
|
||||
send_to_mode:
|
||||
label: Send to
|
||||
type: radio
|
||||
span: left
|
||||
|
||||
send_to_custom:
|
||||
cssClass: radio-align
|
||||
trigger:
|
||||
action: show
|
||||
field: send_to_mode
|
||||
condition: value[custom]
|
||||
|
||||
send_to_admin:
|
||||
label: Send to admin group
|
||||
span: right
|
||||
type: dropdown
|
||||
trigger:
|
||||
action: show
|
||||
field: send_to_mode
|
||||
condition: value[admin]
|
||||
|
||||
reply_to_mode:
|
||||
label: Reply-to address
|
||||
type: radio
|
||||
span: left
|
||||
|
||||
reply_to_custom:
|
||||
cssClass: radio-align
|
||||
trigger:
|
||||
action: show
|
||||
field: reply_to_mode
|
||||
condition: value[custom]
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php namespace RainLab\Notify\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class CreateNotificationRulesTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('rainlab_notify_notification_rules', function ($table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('name')->nullable();
|
||||
$table->string('code')->index()->nullable();
|
||||
$table->string('class_name')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->mediumText('config_data')->nullable();
|
||||
$table->mediumText('condition_data')->nullable();
|
||||
$table->boolean('is_enabled')->default(0);
|
||||
$table->boolean('is_custom')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('rainlab_notify_notification_rules');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php namespace RainLab\Notify\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Schema\Blueprint;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class CreateNotificationsTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('rainlab_notify_notifications', function(Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('event_type');
|
||||
$table->morphs('notifiable');
|
||||
$table->string('icon')->nullable();
|
||||
$table->string('type')->nullable();
|
||||
$table->text('body')->nullable();
|
||||
$table->mediumText('data');
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('rainlab_notify_notifications');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php namespace RainLab\Notify\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Schema\Blueprint;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class CreateRuleActionsTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('rainlab_notify_rule_actions', function(Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('class_name')->nullable();
|
||||
$table->mediumText('config_data')->nullable();
|
||||
$table->integer('rule_host_id')->unsigned()->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('rainlab_notify_rule_actions');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php namespace RainLab\Notify\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Schema\Blueprint;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class CreateConditionsRulesTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('rainlab_notify_rule_conditions', function(Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('class_name')->nullable();
|
||||
$table->mediumText('config_data')->nullable();
|
||||
$table->string('condition_control_type', 100)->nullable();
|
||||
$table->string('rule_host_type', 100)->nullable();
|
||||
$table->integer('rule_host_id')->unsigned()->nullable()->index();
|
||||
$table->integer('rule_parent_id')->unsigned()->nullable()->index();
|
||||
$table->index(['rule_host_id', 'rule_host_type'], 'host_rule_id_type');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('rainlab_notify_rule_conditions');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
v1.0.1:
|
||||
- First version of Notify
|
||||
- create_notifications_table.php
|
||||
- create_notification_rules_table.php
|
||||
- create_rule_conditions_table.php
|
||||
- create_rule_actions_table.php
|
||||
v1.0.2: Fixes crashing bug.
|
||||
v1.0.3: Added Turkish & Russian translations, various bug fixes.
|
||||
v1.1.0: Fixes support for October CMS 2.0
|
||||
v1.1.1: Fixes missing template bug when saving notification rule
|
||||
v1.1.2: Fixes collisions with language keys in compose mail action
|
||||
v1.2.0: Adds delayed execution to actions
|
||||
v1.2.1: Improve support with October v3
|
||||
|
|
@ -61,6 +61,8 @@ class User extends UserBase
|
|||
'transactions' => ['TPS\Birzha\Models\Transaction', 'softDelete' => true],
|
||||
'exchangerequests' => ['TPS\Birzha\Models\Exchangerequest', 'softDelete' => true],
|
||||
'sliders' => ['TPS\Birzha\Models\UserSliders', 'softDelete' => true],
|
||||
'favourites' => ['TPS\Birzha\Models\Favourites', 'softDelete' => true, 'table' => 'tps_birzha_favourites'],
|
||||
'comments' => ['TPS\Birzha\Models\Comment','table' => 'tps_birzha_comments'],
|
||||
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# MIT license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?php namespace RainLab\UserPlus;
|
||||
|
||||
use Yaml;
|
||||
use File;
|
||||
use System\Classes\PluginBase;
|
||||
use RainLab\User\Models\User as UserModel;
|
||||
use RainLab\Notify\Models\Notification as NotificationModel;
|
||||
use RainLab\User\Controllers\Users as UsersController;
|
||||
use RainLab\Notify\NotifyRules\SaveDatabaseAction;
|
||||
use RainLab\User\Classes\UserEventBase;
|
||||
|
||||
/**
|
||||
* UserPlus Plugin Information File
|
||||
*/
|
||||
class Plugin extends PluginBase
|
||||
{
|
||||
|
||||
public $require = ['RainLab.User', 'RainLab.Location', 'RainLab.Notify'];
|
||||
|
||||
/**
|
||||
* Returns information about this plugin.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pluginDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'rainlab.userplus::lang.plugin.name',
|
||||
'description' => 'rainlab.userplus::lang.plugin.description',
|
||||
'author' => 'Alexey Bobkov, Samuel Georges',
|
||||
'icon' => 'icon-user-plus',
|
||||
'homepage' => 'https://github.com/rainlab/userplus-plugin'
|
||||
];
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->extendUserModel();
|
||||
$this->extendUsersController();
|
||||
$this->extendSaveDatabaseAction();
|
||||
$this->extendUserEventBase();
|
||||
}
|
||||
|
||||
public function registerComponents()
|
||||
{
|
||||
return [
|
||||
\RainLab\UserPlus\Components\Notifications::class => 'notifications',
|
||||
];
|
||||
}
|
||||
|
||||
protected function extendUserModel()
|
||||
{
|
||||
UserModel::extend(function($model) {
|
||||
$model->addFillable([
|
||||
'phone',
|
||||
'mobile',
|
||||
'company',
|
||||
'street_addr',
|
||||
'city',
|
||||
'zip'
|
||||
]);
|
||||
|
||||
$model->implement[] = 'RainLab.Location.Behaviors.LocationModel';
|
||||
|
||||
$model->morphMany['notifications'] = [
|
||||
NotificationModel::class,
|
||||
'name' => 'notifiable',
|
||||
'order' => 'created_at desc'
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
protected function extendUsersController()
|
||||
{
|
||||
UsersController::extendFormFields(function($widget) {
|
||||
// Prevent extending of related form instead of the intended User form
|
||||
if (!$widget->model instanceof UserModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$configFile = plugins_path('rainlab/userplus/config/profile_fields.yaml');
|
||||
$config = Yaml::parse(File::get($configFile));
|
||||
$widget->addTabFields($config);
|
||||
});
|
||||
}
|
||||
|
||||
public function registerNotificationRules()
|
||||
{
|
||||
return [
|
||||
'events' => [],
|
||||
'actions' => [],
|
||||
'conditions' => [
|
||||
\RainLab\UserPlus\NotifyRules\UserLocationAttributeCondition::class
|
||||
],
|
||||
'presets' => '$/rainlab/userplus/config/notify_presets.yaml',
|
||||
];
|
||||
}
|
||||
|
||||
protected function extendUserEventBase()
|
||||
{
|
||||
if (!class_exists(UserEventBase::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserEventBase::extend(function($event) {
|
||||
$event->conditions[] = \RainLab\UserPlus\NotifyRules\UserLocationAttributeCondition::class;
|
||||
});
|
||||
}
|
||||
|
||||
protected function extendSaveDatabaseAction()
|
||||
{
|
||||
if (!class_exists(SaveDatabaseAction::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaveDatabaseAction::extend(function ($action) {
|
||||
$action->addTableDefinition([
|
||||
'label' => 'User activity',
|
||||
'class' => UserModel::class,
|
||||
'param' => 'user'
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# User Plus+ plugin
|
||||
|
||||
This plugin extends the [User plugin](http://octobercms.com/plugin/rainlab-user) with extra profile fields and features.
|
||||
|
||||
* Adds the following extra fields to a user: `phone`, `mobile`, `company`, `street_addr`, `city`, `zip`.
|
||||
* A user can belong to a Country and/or State, sourced from the [Location plugin](http://octobercms.com/plugin/rainlab-location).
|
||||
|
||||
### Potential features
|
||||
|
||||
* A user can befriend other users via a friendship system.
|
||||
* A user can earn "Experience Points" by performing predefined activities.
|
||||
|
||||
> Note these features may or may not be implemented in the future, but act only as an indicator of the plugin's potential.
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
.rainlab-userplus {
|
||||
position: relative;
|
||||
}
|
||||
.rainlab-userplus .notifications-popover {
|
||||
background: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
position: absolute;
|
||||
width: 480px;
|
||||
z-index: 2;
|
||||
right: 0;
|
||||
display: none;
|
||||
}
|
||||
.rainlab-userplus.active .notifications-popover {
|
||||
display: block;
|
||||
}
|
||||
.rainlab-userplus .notifications-header {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 15px;
|
||||
}
|
||||
.rainlab-userplus .notifications-header h4 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.rainlab-userplus .notifications-header .close,
|
||||
.rainlab-userplus .notifications-header .mark-all-read {
|
||||
float: right;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.rainlab-userplus .notifications-content .notifications-loading,
|
||||
.notification_area .notifications-loading,
|
||||
.accord_notification .notifications-loading {
|
||||
padding: 20px;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.rainlab-userplus .notifications-content .no-notifications,
|
||||
.notification_area .no-notifications,
|
||||
.accord_notification .no-notifications {
|
||||
text-align: center;
|
||||
padding: 25px;
|
||||
margin: 0;
|
||||
}
|
||||
.rainlab-userplus .notifications-content > ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
overflow: auto;
|
||||
max-height: 400px;
|
||||
}
|
||||
.rainlab-userplus .notifications-content > ul > li {
|
||||
position: relative;
|
||||
padding: 10px 25px 10px 60px;
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
}
|
||||
.rainlab-userplus .notifications-content > ul > li > i {
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
top: 12px;
|
||||
font-size: 18px;
|
||||
opacity: .5;
|
||||
}
|
||||
.rainlab-userplus .notifications-content > ul > li .parsed-body > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.rainlab-userplus .notifications-content > ul > li .date {
|
||||
white-space: nowrap;
|
||||
opacity: .75;
|
||||
}
|
||||
.rainlab-userplus .notifications-footer {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
function toggleNotificationsPopover(el) {
|
||||
|
||||
if($(el).next().hasClass('notification_area')) {
|
||||
$(el).request('onLoadNotifications', {
|
||||
update: { '@notifications-list': '#notification_area' }
|
||||
})
|
||||
} else if($(el).next().hasClass('accord_notification')) {
|
||||
$(el).request('onLoadNotifications', {
|
||||
update: { '@notifications-list': '#accord_notification' }
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue