I would like to add a custom notification to my buddypress "notification" tab when a particular event occurs. How to achieve this?
I followed this tutorial and is very complete. Worked for me
BuddyPress: Adding Custom Notifications
I am going to put that the author wrote. But is better if you go to the post directly, there you can find a much better explanation. I think that the post is for dummies, very complete and explanatory, even has a gist.
1st register your component
You need to register your notification as a budypress component. This is very easy. The name of the component registered was custom
// this is to add a fake component to BuddyPress. A registered component is needed to add notifications
function custom_filter_notifications_get_registered_components( $component_names = array() ) {
// Force $component_names to be an array
if ( ! is_array( $component_names ) ) {
$component_names = array();
}
// Add 'custom' component to registered components array
array_push( $component_names, 'custom' );
// Return component's with 'custom' appended
return $component_names;
}
add_filter( 'bp_notifications_get_registered_components', 'custom_filter_notifications_get_registered_components' );
2nd Render the notification
// this gets the saved item id, compiles some data and then displays the notification
function custom_format_buddypress_notifications( $action, $item_id, $secondary_item_id, $total_items, $format = 'string' ) {
// New custom notifications
if ( 'custom_action' === $action ) {
$comment = get_comment( $item_id );
$custom_title = $comment->comment_author . ' commented on the post ' . get_the_title( $comment->comment_post_ID );
$custom_link = get_comment_link( $comment );
$custom_text = $comment->comment_author . ' commented on your post ' . get_the_title( $comment->comment_post_ID );
// WordPress Toolbar
if ( 'string' === $format ) {
$return = apply_filters( 'custom_filter', '' . esc_html( $custom_text ) . '', $custom_text, $custom_link );
// BuddyBar
} else {
$return = apply_filters( 'custom_filter', array(
'text' => $custom_text,
'link' => $custom_link
), $custom_link, (int) $total_items, $custom_text, $custom_title );
}
return $return;
}
}
add_filter( 'bp_notifications_get_notifications_for_user', 'custom_format_buddypress_notifications', 10, 5 );
3st Launch the Notification
Here you add the notification when someone writes you on a post. Use the action hook wp_insert_comment for catch that event.
// this hooks to comment creation and saves the comment id
function bp_custom_add_notification( $comment_id, $comment_object ) {
$post = get_post( $comment_object->comment_post_ID );
$author_id = $post->post_author;
bp_notifications_add_notification( array(
'user_id' => $author_id,
'item_id' => $comment_id,
'component_name' => 'custom',
'component_action' => 'custom_action',
'date_notified' => bp_core_current_time(),
'is_new' => 1,
) );
}
add_action( 'wp_insert_comment', 'bp_custom_add_notification', 99, 2 );
All together
<?php
// this is to add a fake component to BuddyPress. A registered component is needed to add notifications
function custom_filter_notifications_get_registered_components( $component_names = array() ) {
// Force $component_names to be an array
if ( ! is_array( $component_names ) ) {
$component_names = array();
}
// Add 'custom' component to registered components array
array_push( $component_names, 'custom' );
// Return component's with 'custom' appended
return $component_names;
}
add_filter( 'bp_notifications_get_registered_components', 'custom_filter_notifications_get_registered_components' );
// this gets the saved item id, compiles some data and then displays the notification
function custom_format_buddypress_notifications( $action, $item_id, $secondary_item_id, $total_items, $format = 'string' ) {
// New custom notifications
if ( 'custom_action' === $action ) {
$comment = get_comment( $item_id );
$custom_title = $comment->comment_author . ' commented on the post ' . get_the_title( $comment->comment_post_ID );
$custom_link = get_comment_link( $comment );
$custom_text = $comment->comment_author . ' commented on your post ' . get_the_title( $comment->comment_post_ID );
// WordPress Toolbar
if ( 'string' === $format ) {
$return = apply_filters( 'custom_filter', '' . esc_html( $custom_text ) . '', $custom_text, $custom_link );
// Deprecated BuddyBar
} else {
$return = apply_filters( 'custom_filter', array(
'text' => $custom_text,
'link' => $custom_link
), $custom_link, (int) $total_items, $custom_text, $custom_title );
}
return $return;
}
}
add_filter( 'bp_notifications_get_notifications_for_user', 'custom_format_buddypress_notifications', 10, 5 );
// this hooks to comment creation and saves the comment id
function bp_custom_add_notification( $comment_id, $comment_object ) {
$post = get_post( $comment_object->comment_post_ID );
$author_id = $post->post_author;
bp_notifications_add_notification( array(
'user_id' => $author_id,
'item_id' => $comment_id,
'component_name' => 'custom',
'component_action' => 'custom_action',
'date_notified' => bp_core_current_time(),
'is_new' => 1,
) );
}
add_action( 'wp_insert_comment', 'bp_custom_add_notification', 99, 2 );
You use bp_notifications_add_notification(). The following example function is hooked to bp_activity_sent_mention_email - So when an email notification is sent due to somebody being #-mentioned, a core notification is generated.
function bp_activity_at_mention_add_notification( $activity, $subject, $message, $content, $receiver_user_id ) {
if ( bp_is_active( 'notifications' ) ) {
bp_notifications_add_notification( array(
'user_id' => $receiver_user_id,
'item_id' => $activity->id,
'secondary_item_id' => $activity->user_id,
'component_name' => buddypress()->activity->id,
'component_action' => 'new_at_mention',
'date_notified' => bp_core_current_time(),
'is_new' => 1,
) );
}
}
add_action( 'bp_activity_sent_mention_email', 'bp_activity_at_mention_add_notification', 10, 5 );
Ref: http://codex.buddypress.org/developer/function-examples/bp_notifications_add_notification/
Related
With this function I can add custom sku to simple product, then when order is Placed I see this custom sku in API order:
// Add Custom SKU Field
function my_add_custom_sku() {
$args = array(
'label' => __( 'ArticleID', 'woocommerce' ),
'placeholder' => __( 'Enter Visma ArticleID Here', 'woocommerce' ),
'id' => 'articleid',
'desc_tip' => true,
'description' => __( 'Visma ArticleID is for integration with Zapier.', 'woocommerce' ),
);
woocommerce_wp_text_input( $args );
}
add_action( 'woocommerce_product_options_sku', 'my_add_custom_sku' );
// Save
function my_save_custom_meta( $product ){
if( isset($_POST['articleid']) ) {
$product->update_meta_data( 'articleid', sanitize_text_field( $_POST['articleid'] ) );
}
}
add_action( 'woocommerce_admin_process_product_object', 'my_save_custom_meta', 10, 1 );
// Save as custom order item meta data and display on orders and email notifications
add_action( 'woocommerce_checkout_create_order_line_item', 'add_articleid_on_orders_and_emails', 10, 4 );
function add_articleid_on_orders_and_emails( $item, $cart_item_key, $values, $order ) {
$articleid = $values['data']->get_meta('articleid'); // Get product "articleid"
// For product variations when the "articleid" is not defined
if ( empty($articleid) && $values['variation_id'] > 0 ) {
$product = wc_get_product( $values['product_id'] ); // Get the parent variable product
$articleid = $product->get_meta( 'articleid' ); // Get parent product "articleid"
}
if ( ! empty($articleid) ) {
$item->add_meta_data( 'articleid', $articleid ); // add it as custom order item meta data
}
}
// Replace the label (slug) by a readable label name on orders and emails
add_filter( 'woocommerce_display_item_meta', 'filter_order_item_articleid_displayed_label', 100, 3 );
function filter_order_item_articleid_displayed_label( $html, $item, $args ) {
// Not on admin
if ( ! is_admin() && $item->get_meta('articleid') ) {
$html = str_replace('articleid', __('ArticleID', 'woocommerce'), $html);
}
return $html;
}
Now output API orders:
wp-json/wc/v3/orders
Output:
"meta_data": [
{
"id": 90,
"key": "articleid",
"value": "testReplace",
"display_key": "articleid",
"display_value": "testReplace"
}
How to also add custom sku for product varints and when order is placed then also display in orders API in meta_data ?
I am using bootstrap ActiveForm. Here is my form:
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use kartik\file\FileInput;
/* #var $this yii\web\View */
/* #var $model common\models\Customer */
/* #var $form yii\widgets\ActiveForm */
?>
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'],
'id' => 'customer-form',
'enableClientValidation' => true,
'options' => [
'validateOnSubmit' => true,
'class' => 'form'
],
'layout' => 'horizontal',
'fieldConfig' => [
'horizontalCssClasses' => [
'label' => 'col-sm-4',
// 'offset' => 'col-sm-offset-2',
'wrapper' => 'col-sm-8',
],
],
]); ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?php ActiveForm::end(); ?>
Here is my model:
class Customer extends \yii\db\ActiveRecord
{
public $username;
public $password;
public $status;
public $email;
public $uploads;
public function rules()
{
return [
[['user_id', 'created_by', 'updated_by'], 'integer'],
[['created_at','uploads', 'updated_at','legacy_customer_id','fax','phone_two','trn'], 'safe'],
[['company_name','customer_name','username','password', 'tax_id'], 'string', 'max' => 200],
[['customer_name','email','legacy_customer_id','company_name','city'], 'required'],
[['is_deleted','status'], 'boolean'],
[['address_line_1', 'state','phone', 'country'], 'string', 'max' => 450],
[['address_line_2', 'city', 'zip_code'], 'string', 'max' => 45],
[['user_id','legacy_customer_id'], 'unique'],
['email', 'email'],
[['uploads'], 'file', 'maxFiles' => 10],
[['email'], 'unique', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['email' => 'email'], 'message' => 'This email address has already been taken.'],
[['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
];
}
}
Here is Controller
public function actionCreate() {
$model = new Customer();
if ($model->load(Yii::$app->request->post())) {
$transaction = Yii::$app->db->beginTransaction();
try
{
$user_create = \common\models\User::customeruser($model);
if ($user_create) {
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole('customer');
$auth->assign($role, $model->user_id);
}
if ($user_create && $model->save()) {
$photo = UploadedFile::getInstances($model, 'uploads');
if ($photo !== null) {
$save_images = \common\models\CustomerDocuments::save_document($model->user_id, $photo);
}
$transaction->commit();
return $this->redirect(['view', 'id' => $model->user_id]);
}
}catch (Exception $e)
{
$transaction->rollBack();
}
}
if (Yii::$app->request->isAjax) {
return $this->renderAjax('create', [
'model' => $model,
]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
Now the required attribute working used in the rules. It does not allow the form to submit until the required field filled with some value, but at the same time the unique attribute using with target class not working and allow the form to submit. After click the submit form the form will not submit but it does not show the error of unique validation. Regard I am using the form in bootstrap modal and I want the form will show the unique submit error before submission like the same as required working. I can do it using jQuery on blur function and send custom AJAX request but I want the default solution of Yii 2.
EDIT
This is where the error is throw due to user not being saved
public static function customeruser( $model ) {
$user = new User();
$user->username = $model->email;
$user->email = $model->email;
$user->setPassword ( $model->legacy_customer_id );
$user->generateAuthKey ();
if ( !$user->save () ) {
var_dump ( $user->getErrors () );
exit ();
} return $user->save () ? $user : null;
}
var_dump() shows the following
'username' => array (size = 1)
0 => string 'This username has already been taken.' (length = 37)
'email' => array (size = 1) 0 => string 'This email address has already been taken.' (length = 42).
As you are using the try catch block along with the transaction you should throw and catch such errors as the exception so that the transaction is rolled back, and the message is displayed to the user too.
You are not consuming or using the beauty of try{}catch(){} block with transactions. You should always throw an Exception in case any of the models are not saved and the catch block will rollback the transaction.
For example, you are saving the user in the function customeruser() by calling
$user_create = \common\models\User::customeruser($model);
and returning the user object or null otherwise and then in the very next line, you are verifying the user was created or not.
if ($user_create) {
You should simply throw an exception from the function customeruser() in case the model was not saved and return the $user object otherwise, you don't have to check $user_create again to verify if user was not saved the exception will be thrown and control will be transferred to the catch block and the lines after $user_create = \common\models\User::customeruser($model); will never be called.
I mostly use the following way when i have multiple models to save and i am using the transaction block.
$transaction = Yii::$app->db->beginTransaction ();
try {
if ( !$modelUser->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelUser->errors , 0 , false ) ) );
}
if ( !$modelProfile->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelProfile->errors , 0 , false ) ) );
}
$transaction->commit();
} catch ( \Exception $ex ) {
$transaction->rollBack();
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
}
So you can do the same for your code
public static function customeruser( $model ) {
$user = new User();
$user->username = $model->email;
$user->email = $model->email;
$user->setPassword ( $model->legacy_customer_id );
$user->generateAuthKey ();
if(!$user->save()){
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $user->errors , 0 , false ) ) );
}
return $user;
}
change your actionCreate to the following
public function actionCreate() {
$model = new Customer();
if ( $model->load ( Yii::$app->request->post () ) ) {
$transaction = Yii::$app->db->beginTransaction ();
try {
$user_create = \common\models\User::customeruser ( $model );
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole ( 'customer' );
$auth->assign ( $role , $model->user_id );
if ( !$model->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) );
}
$photo = UploadedFile::getInstances ( $model , 'uploads' );
if ( $photo !== null ) {
$save_images = \common\models\CustomerDocuments::save_document ( $model->user_id , $photo );
}
$transaction->commit ();
return $this->redirect ( [ 'view' , 'id' => $model->user_id ] );
} catch ( \Exception $ex ) {
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
$transaction->rollBack ();
}
}
if ( Yii::$app->request->isAjax ) {
return $this->renderAjax ( 'create' , [
'model' => $model ,
] );
}
return $this->render ( 'create' , [
'model' => $model ,
] );
}
There are two three thing :
1. You are not accepting email address by user input .
2. It will insert a blank entry if in database your field is taken as NULL
3. In second insertion it will show you the error message if db field is not NUll.
Solution:
1. Make db field Not NUll .
2. Take email as user input .
3. Try to print error
if ($model->validate()) {
// all inputs are valid
} else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
4. remove 'skipOnError' => true,
Try Above I am sure you will get the solution
Help Full LINK For validation
I want to upload an image via Kartik widget. After submitting the form, the $_FILE['Product'] has the data about the image but getInstance($model, 'images') returns null. Tried with images[], also null.
This is what I'm trying to var_dump in the controller:
public function actionCreate()
{
$model = new Product();
if ($model->load(Yii::$app->request->post())) {
var_dump(UploadedFile::getInstance($model, 'images[]'));die;
And this is my model Product:
<?php
namespace app\models;
use backend\models\CActiveRecord;
use Yii;
use omgdef\multilingual\MultilingualQuery;
use omgdef\multilingual\MultilingualBehavior;
use yii\web\UploadedFile;
/**
* This is the model class for table "product".
*
* #property int $id
* #property int $category_id
* #property int $quantity
* #property double $price
* #property int $sort
*
* #property Productlang[] $productlangs
*/
class Product extends CActiveRecord
{
public $images;
public static function find()
{
return new MultilingualQuery(get_called_class());
}
public function behaviors()
{
$allLanguages = [];
foreach (Yii::$app->params['languages'] as $title => $language) {
$allLanguages[$title] = $language;
}
return [
'ml' => [
'class' => MultilingualBehavior::className(),
'languages' => $allLanguages,
//'languageField' => 'language',
//'localizedPrefix' => '',
//'requireTranslations' => false',
//'dynamicLangClass' => true',
//'langClassName' => PostLang::className(), // or namespace/for/a/class/PostLang
'defaultLanguage' => Yii::$app->params['languageDefault'],
'langForeignKey' => 'product_id',
'tableName' => "{{%productLang}}",
'attributes' => [
'title',
'description',
'meta_title',
'meta_desc',
'url'
]
],
];
}
/**
* #inheritdoc
*/
public static function tableName()
{
return 'product';
}
/**
* #inheritdoc
*/
public function rules()
{
$string = $this->multilingualFields(['description', 'url']);
$string_59 = $this->multilingualFields(['meta_title']);
$string_255 = $this->multilingualFields(['meta_desc', 'title']);
$string[] = 'description';
$string[] = 'url';
$string_59[] = 'meta_title';
$string_255[] = 'meta_desc';
$string_255[] = 'title';
return [
[['quantity', 'price', 'title', 'meta_title', 'meta_desc'], 'required'],
[['category_id', 'quantity', 'sort'], 'integer'],
[$string, 'string'],
[$string_59, 'string', 'max' => 59],
[$string_255, 'string', 'max' => 255],
[['price'], 'number'],
['images', 'file']
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'category_id' => 'Category ID',
'quantity' => 'Quantity',
'price' => 'Price',
'sort' => 'Sort',
];
}
public function upload()
{
if ($this->validate()) {
foreach ($this->image as $file) {
$file->saveAs(\Yii::getAlias("#images") . "/products/" . $this->id . "_" . $this->image->baseName . '.' . $this->image->extension);
}
return true;
} else {
return false;
}
}
}
Tried with rules ['images', 'safe'] also ['images', 'file'] ( think the second one is not right because the attribute is an array, right ? ). The form is <?php $form = ActiveForm::begin(['options' => ['multipart/form-data']]); ?>.
Finally my input:
<?= $form->field($model, 'images[]')->widget(FileInput::class, [
'showMessage' => true,
]) ?>
Full controller action:
public function actionCreate()
{
$model = new Product();
if ($model->load(Yii::$app->request->post())) {
foreach (Yii::$app->params['languages'] as $language){
if(Yii::$app->params['languageDefault'] != $language){
$title_lang = "title_$language";
$model->$title_lang = Yii::$app->request->post('Product')["title_$language"];
$description_lang = "description_$language";
$model->$description_lang = Yii::$app->request->post('Product')["description_$language"];
$meta_title_lang = "meta_title_$language";
$model->$meta_title_lang = Yii::$app->request->post('Product')["meta_title_$language"];
$meta_desc_lang = "meta_desc_$language";
$model->$meta_desc_lang = Yii::$app->request->post('Product')["meta_desc_$language"];
}
}
if($model->save()){
$model = $this->findModel($model->id, true);
//Make urls
foreach (Yii::$app->params['languages'] as $language) {
if (Yii::$app->params['languageDefault'] != $language) {
$url_lang = "url_$language";
$title_lang = "title_$language";
$model->$url_lang = $model->constructURL(
$model->$title_lang,
$model->id
);
}else{
$model->url = $model->constructURL(
$model->title,
$model->id
);
}
}
//Upload Images
$model->images = UploadedFile::getInstance($model, 'images');
if (!($model->upload())) {
Yii::$app->session->setFlash('error', Yii::t('app', 'Some problem with the image uploading occure!'));
return $this->redirect(['create']);
}
if($model->update() !== false){
return $this->redirect(['view', 'id' => $model->id]);
}else{
Yii::$app->session->setFlash('error', Yii::t('app', 'Something went wrong. Please, try again later!'));
return $this->redirect(['create']);
}
}
}
return $this->render('create', [
'model' => $model,
]);
}
What looks like you are trying to upload a single image you should remove the [] from the input field name from the ActiveForm field declaration, and from models rules.
Single File
<?= $form->field($model, 'images')->widget(FileInput::class, [
'showMessage' => true,
'pluginOptions' => [
'showCaption' => false ,
'showRemove' => false ,
'showUpload' => false ,
'showPreview' => false ,
'browseClass' => 'btn btn-success btn-block' ,
'browseIcon' => '<i class="glyphicon glyphicon-camera"></i> ' ,
'browseLabel' => 'Select Profile Image'
] ,
'options' => ['accept' => 'image/*' ] ,
]) ?>
and from the following line
UploadedFile::getInstance($model, 'images');
Multiple Files
For multiple files you need to add 'options' => ['multiple' => true] for the field and change the attribute name to images[]
<?= $form->field($model, 'images[]')->widget(FileInput::class, [
'showMessage' => true,
'pluginOptions' => [
'showCaption' => false ,
'showRemove' => false ,
'showUpload' => false ,
'showPreview' => false ,
'browseClass' => 'btn btn-success btn-block' ,
'browseIcon' => '<i class="glyphicon glyphicon-camera"></i> ' ,
'browseLabel' => 'Select Profile Image'
] ,
'options' => ['accept' => 'image/*' ,'multiple'=>true] ,
]) ?>
and for receiving the uploaded files you should not specify the attribute as an array just change getInstance to getInstances and then try printing, it will show you all the images use foreach() to save all of them.
UploadedFile::getInstances($model, 'images');
I personally prefer to use a separate model for file uploading rather than using the ActiveRecord model.
Note: When using multiple files upload, you can also specify the 'maxFiles'=>1000 inside you model rules to limit the number of files to be uploaded
EDIT
For troubleshooting your code you should comment out the actionCreate from the controller and replace with the one i added below
public function actionCreate() {
$model = new Product();
if ( $model->load ( Yii::$app->request->post () ) ) {
foreach ( Yii::$app->params['languages'] as $language ) {
if ( Yii::$app->params['languageDefault'] != $language ) {
$title_lang = "title_$language";
$model->$title_lang = Yii::$app->request->post ( 'Product' )["title_$language"];
$description_lang = "description_$language";
$model->$description_lang = Yii::$app->request->post ( 'Product' )["description_$language"];
$meta_title_lang = "meta_title_$language";
$model->$meta_title_lang = Yii::$app->request->post ( 'Product' )["meta_title_$language"];
$meta_desc_lang = "meta_desc_$language";
$model->$meta_desc_lang = Yii::$app->request->post ( 'Product' )["meta_desc_$language"];
}
}
$transaction = Yii::$app->db->beginTransaction ();
try {
if ( !$model->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) );
}
//Make urls
foreach ( Yii::$app->params['languages'] as $language ) {
if ( Yii::$app->params['languageDefault'] != $language ) {
$url_lang = "url_$language";
$title_lang = "title_$language";
$model->$url_lang = $model->constructURL (
$model->$title_lang , $model->id
);
} else {
$model->url = $model->constructURL (
$model->title , $model->id
);
}
}
//save the new urls
if ( !$model->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) );
}
//Upload Images
$model->images = UploadedFile::getInstances ( $model , 'images' );
$model->upload ();
//commit the transatction to save the record in the table
$transaction->commit ();
Yii::$app->session->setFlash ( 'success' , 'The model saved successfully.' );
return $this->redirect ( [ 'view' , 'id' => $model->id ] );
} catch ( \Exception $ex ) {
$transaction->rollBack ();
Yii::$app->session->setFlash ( 'error' , Yii::t ( 'app' , $ex->getMessage () ) );
}
}
return $this->render ( 'create' , [
'model' => $model ,
] );
}
And comment out the upload() function of your model and add below function
public function upload() {
$skipped = [];
foreach ( $this->images as $file ) {
if ( !$file->saveAs ( \Yii::getAlias ( "#images" ) . "/products/" . $this->id . "_" . $this->image->baseName . '.' . $this->image->extension ) ) {
$skipped[] = "File " . $file->baseName . " was not saved.";
}
}
if ( !empty ( $skipped ) ) {
Yii::$app->session->setFlash ( 'error' , implode ( "<br>" , $skipped ) );
}
}
And for the ActiveForm make sure your input matches the following
$form->field($model, 'images[]')->widget(FileInput::class, [
'showMessage' => true,
'pluginOptions' => [
'showCaption' => false ,
'showRemove' => false ,
'showUpload' => false ,
'showPreview' => false ,
'browseClass' => 'btn btn-success btn-block' ,
'browseIcon' => '<i class="glyphicon glyphicon-camera"></i> ' ,
'browseLabel' => 'Select Profile Image'
] ,
'options' => ['accept' => 'image/*','multiple'=>true ] ,
]) ;
I have a plugin that I created and I want to use the WP rest api controller pattern and extend the api.
<?php
/**
* Plugin Name: myplugin
* Plugin URI: h...
* Description: A simple plugin ...
* Version: 0.1
* Author: Kamran ...
* Author ....
* License: GPL2
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items();
$items->register_routes();
}
add_action('rest_api_init','myplugin_register_endpoints');
.
.
I created a class in folder called server/controllers and inside it my_ctrl.php file with a class that extends WP_REST_Controller that looks like this
<?php
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
$version = '1';
$namespace = 'my-namespase/v' . $version;
$base = 'abc';
register_rest_route( $namespace, '/' . $base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
) );
register_rest_route( $namespace, '/' . $base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
) );
register_rest_route( $namespace, '/' . $base . '/schema', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_public_item_schema' ),
) );
}
function get_items( $request ){
return new WP_REST_Response( array('message' => "list items"), 200 );
}
function create_item( $request ) {
.....
if($author_email==$user_email) {
return new WP_REST_Response( array('message' => 'success', 200 );
} else {
return new WP_Error('my-error', __(' error...','abc'), array( 'status' => 500 ));
}
}
//Remove vote////////////////////////////////////////////
function delete_item( $request ) {
...
if($author_email==$user_email) {
return new WP_REST_Response( array('message' => 'success', 200 );
} else {
return new WP_Error('my-error', __(' error...','abc'), array( 'status' => 500 ));
}
}
public function get_items_permissions_check( $request ) {
return true;
}
public function create_item_permissions_check( $request ) {
if ( !is_user_logged_in()) {
return new WP_Error('login error',__('You are not logged in','KVotes-voting'));
}
return true;
}
public function delete_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request );
}
protected function prepare_item_for_database( $request ) {
return array();
}
public function prepare_item_for_response( $item, $request ) {
return array();
}
public function get_collection_params() {
return array(
'page' => array(
'description' => 'Current page of the collection.',
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
),
'per_page' => array(
'description' => 'Maximum number of items to be returned in result set.',
'type' => 'integer',
'default' => 10,
'sanitize_callback' => 'absint',
),
'search' => array(
'description' => 'Limit results to those matching a string.',
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
);
}
}
I am logged in and I am using cookie authentication with Nonce in my plugin.
when I run my code and debug it with sublime xdebug extension I can see that I indeed hit the end points routes but although I am logged it in the lines: "is_user_logged_in()" = (bool) 0 and therefore the function create_item_permissions_check return new WP_Error(....);and not true;
therefore my rest callback "create_item" is not invoked, I don't understand why is_user_logged_in() return false even when I am logged in.
The solution was to send the logged in user info to my custom class as a parameter to the constructor and then use the user data in the permission check function and other functions that needs the user info:
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
private $loged_in;//bool
private $user;
public function __construct($logged,$cur_user) {
= $logged;
$this->user = $cur_user;
}
.
.
.
public function create_item_permissions_check( $request ) {
if($this->loged_in!=1){
return new WP_Error('login error',__('You are not logged in','....'));
}
return true;
}
.
.
.
}
And my plugin myplugin_register_endpoints looks as follows:
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items(is_user_logged_in(),wp_get_current_user());
$items->register_routes();
}
now when I route to one of the URL's And hit the end points and the check permission is invoked with the needed user data. $this->loged_in!=1 when thew user is not logged in, otherwise the permission check returns true .
I ran into precisely this issue. It seems that the architecturally correct way to handle the problem is for the ajax request to include a nonce. See the references discussed in this answer
If the response does not contain data about the city, I want to see the output message. Also i want to change css of text in textfield when I get an empty response.
I have:
View:
$this->widget('zii.widgets.jui.CJuiAutoComplete', array(
'name'=>'city_id',
'value'=>'',
'source'=>CController::createUrl('/PromouterCity/autoComplete'),
'options'=>array(
'showAnim'=>'fold',
'minLength'=>'0',
'select'=>'js:function( event, ui ) {
$("#city_id").val( ui.item.name );
$("#selectedvalue").val( ui.item.id);
return false;
}',
),
'placeholder' => "Search...",
),
));
Controller:
public function actionAutoComplete(){
$match=$_GET['term'];
if(!empty($match)){
$match = addcslashes($match, '%_');
$q = new CDbCriteria( array(
'condition' => "name LIKE :match",
'params' => array(':match' => "$match%")
));
$query = City::model()->findAll($q);
}else{
$query=array(
'0'=>array(
'id'=>'1',
'name'=>'London',
)
);
}
$list = array();
foreach($query as $q){
$data['label']=$q['name'];
$data['id']= $q['id'];
$data['name']= $q['name'];
$list[]= $data;
unset($data);
}
echo CJSON::encode($list);
Yii::app()->end();
}
Decision:
'response'=> 'js:function( event, ui ) {
if (ui.content.length === 0) {
$("#empty-message").text("your message");
} else {
$("#empty-message").empty();
}
}',