Skip Navigation

[Resolved] implement favorites system with toolset

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

This topic contains 61 replies, has 4 voices.

Last updated by simonM-5 1 day, 20 hours ago.

Assigned support staff: Jamal.

Author
Posts
#1789443

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Sure 🙂

#1794635

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

As we are still working to check why the relationships are not appearing on the UI, I'd like to set this ticket as waiting for your feedback. Or I can provide a test site and we can work on this in parallel.

#1802025

HI Jamal

How would you like to proceed here? Are you planning to create a prototype in another environment?

Looking forward to hearing from you.

Kind regards
Simon

#1802811

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Hello Simon,

I would like that we work on a clean install, but if you prefer to work on your dev server, I'll still try to help. I am not sure that we will be able to honor all of the requirements, but we'll find out how to sync the favorites across languages from any origin language.

As per your suggestion, it seems that relying on a one-to-many relationship is a good solution. Then we'll see how to implement the favorite/unfavorite flags forms to use icons instead of a regular form.
I thought, that we can reuse some code of our previous solution, so we can insert a form(favoriting) of the original post when we are on a duplicate.

So, I'll let you prepare the relationship, a view that will search for favorites from the current user on this post(job), so we can then either show a form to favorite, or to unfavorite. And we'll see how to come up with the syncing code. Sounds good?

If you prefer that we work on a test site on our platform, let me know and I'll prepare one with the required plugins.

#1807015

Hi Jamal

Our site is experiencing issues with updating code snippets. Waqar is working on solving that. So I can' t update any custom code snippets via the GUI at the moment.

So I think it's fine if you start on a clean install and then adapt it on our site as we go along.

Kind regards
Simon

#1807069

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Hello Simon,

Of course, we can do that. I created a new installation on my server and I registered it with both Toolset and WPML. You can install the required plugins from our installer page here hidden link

Credentials to connect are on my previous private reply(October 10, 2020 at 3:43 pm).

Let me know when you prepare the needed elements(CTPs, relationships, etc...)

#1809203

Hi Jamal

Thanks for setting up the prototype Favourites site. I logged in and changed my password to something more memorable. 🙂

OK, so I made some changes so far:

(1) Set up the WPML to have English (US) as default language (this was what you installed - our site uses normal English, but that's OK for now) and German as secondary language.

(2) Added addtional plugins:
- Toolset & WPML Plugins
- Font Awesome
- Admin Columns

(3) Created 3 Post Types:
- Job Ads
- Nanny Ads
- Favourites

(4) Added Views to display some fake Job Ads and Nanny Ads:
- My Job Ads View (my-job-ads-view)
- My Nanny Ads View (my-nanny-ads-view)
Question here: Can we create the Job/Nanny Ads on the backend (ours are normally created on submission of a Toolset Post Form - should I create some basic Post Forms to mimic what we do on our site, or is sufficient to submit them from the back end? In other words, would submission from the back end be enough to trigger a language duplicate?)

(5) Created 2 new Pages to display these Views:
- Find a Job (find-a-job)
- Find a Nanny (find-a-nanny)

(6) Added following custom fields in Field Group for Favourites:
- Logged in User ID (logged-in-user-id)
- Favourited Job Ad ID (favourited-job-ad-id)
- Favourited Nanny Ad ID (favourited-nanny-ad-id)

(7) Created simple 4 Post Forms (these are intended to create the Favourite/Unfavourite button as an icon):
- Favourite Job Ad
- Unfavourite Job Ad
- Favourite Nanny Ad
- Unfavourite Nanny Ad
(7a) Here I couldn't get the button to display as the Font Awesome heart icon unfortunately. Not sure why that it's not accepting the input as the [cred_field] value argument, so for now have just changed it to text "Favourite this Job Ad"... However we would like to have the simple heart icons instead of the text button.

Here I wanted to pause and wait for your advice, before I start building unnecessary stuff...

My thinking is that the Favourites CPT will contain 4 columns, its own ID, then the 3 columns described in (6).
So on submitting the Favourite Job Ad Post Form, for example, I would imagine a row going into the database for the Favourites CPT with the logged in user's ID, and the Job Ad ID. The Nanny Ad ID for that row would be NULL. Similary for submitting Nanny Ads, the Job Ad ID would be NULL.
If my thinking is correct then we could build 2 simple Views to retrieve all favourites for the logged in user where either Job Ad ID is not null or where Nanny Ad ID is not null.
Perhaps it would make sense to add a language column to the Favourites CPT to store the language of the original Job/Nanny Ad? It might be useful later...?

OK, I'll stop here and wait for feedback before I continue with anything, especially the relationships. Let me know how you would like to proceed.

Thanks and best regards
Simon

#1810007

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Hello Simon,

Thank you for your collaboration. I suggest that we try to reproduce the same use case at your website and that we create Nanny Ads or Job Ads from the frontend using a form. Why? To include the same custom code that is required to duplicate the post to other languages.
We can try working from the backend, but we'll need a different custom code to duplicate the posts.
You can create a simple form that only has the title, and you will need to add the custom code to duplicate it.

Also, please, add the custom code that we have produced in order to check if a post is original or duplicate. Because we'll rely on it to put the (favorite/unfavorite)forms bound to the original post when we are visiting a duplicate.

#1810023

Hi Jamal

Have created 2 new Post Forms
- New Nanny Ad (35)
- New Job Ad (33)

Added the two custom codes.

Created new Ads from the front end. The duplication appears to be working OK. Welcome to test yourself.

Best regards
Simon

#1811019

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Thank you very much, Simon.

As I'll need to produce custom code, I prefer to debug it locally. So, I am taking a copy of this website and I'll work on it locally, then I'll bring my results here and write a reply about it to explain the steps I am following.

I'll get back to you as soon as possible.

#1812149

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Hello Simon,

I just want to let you know that I am still working on this issue. I am finding some troubles, so I decided to, first, work without WPML, then integrate with WPML. I'll get back to you as soon as possible.

#1812517

Hi Jamal

No worries, just shoot me an update when you need me again. I still can't edit Code Snippets via front end on our site due to conflicts with WordFence Firewall and settings which my host needs to set. Unfortunately I can't reach my host support very well at the moment, so I am a little bit in limbo at the moment. So you have time! 🙂

Kind regards
Simon

#1815905

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Hello Simon,

Implementing the favorite feature and integrating it with WPML will require the website to be able to add custom code. If it is not possible yet, through Toolset->Settings->Custom Code section, we can add the custom code to the theme's functions.php file. And that's what I used on the test site.

After playing with the website locally last week, I came to the conclusion, that Toolset, currently, is not suited well to implement the favorite feature. Primarily, because we do not have a Javascript API around AJAX calls in Toolset Forms.

The favorite feature, in my opinion, must be implemented in AJAX. But, Toolset Forms does not offer an AJAX API, so we can manipulate the Favorite/Unfavorite forms after the action(form submitted). Even, if we can come up with some workarounds, we'll hit the wall very soon.

If, Instead, we try to build the feature without AJAX, Toolset Forms, do not offer a way to redirect the form to the previous page. We can work around it using the cred_success_redirect hook. But this will create two other issues:
- When coming from an AJAX filtered view, that does not update the URL, we'll get redirected to an unfiltered view.
- If the Favorite/Unfavorite action did not succeed, we'll get an unexpected redirect.
https://toolset.com/documentation/programmer-reference/cred-api/#cred_success_redirect

There is also the requirement of using heart icons, which is not possible, currently, without some custom coding too.

Toolset is a powerful solution to build complex and amazing solutions, I believe it now more than ever, especially after playing around with this use case. But, until we have an AJAX/Javascript API around forms, we should go with a different solution.

On the other hand, I was able to find another plugin that has an easy, but undocumented API, to easily build this use case.
https://wordpress.org/plugins/wp-favorite-posts/#installation
I was able to integrate it with your use of WPML with few lines of custom code, check the theme's functions.php file:

add_action('init', function(){
	if (function_exists('wpfp_link')) {
		add_action('wpfp_after_add', 'jts_sync_favorite', 10, 1);
		add_action('wpfp_after_remove', 'jts_sync_unfavorite', 10, 1);
	}
  });
  
function jts_sync_favorite( $post_id ) {
	$posts = get_duplicates( $post_id );
	foreach( $posts as $land => $post ) {
		if ( $post->element_id !== $post_id ) {
			remove_action('wpfp_after_add', 'jts_sync_favorite');
			wpfp_add_favorite( $post->element_id );
		}
	}
}
  
function jts_sync_unfavorite( $post_id ) {
	$posts = get_duplicates( $post_id );
	foreach( $posts as $land => $post ) {
		if ( $post->element_id !== $post_id ) {
			remove_action('wpfp_after_remove', 'jts_sync_unfavorite');
			wpfp_remove_favorite( $post->element_id );
		}
	}
}

function get_duplicates( $post_id){
	$args = array('element_id' => $post_id, 'element_type' => 'job-ad' );
	$info = apply_filters( 'wpml_element_language_details', null, $args );
	$post = get_post( $post_id );
	$posts = apply_filters( 'wpml_get_element_translations', NULL, $info->trid, 'post_' . $post->post_type );
	return $posts;
}

I was also able to use FontAwesome icons for the form easily with the plugin's configuration. Check this screenshot hidden link

The plugin allows putting the form using a shortcode https://wordpress.org/plugins/wp-favorite-posts/#installation
And offers another shortcode to get the list of favorite posts.

[wpfp-link] for the form.
[wp-favorite-posts] for the list of favorites.

You can check how it is working here hidden link
Switch languages from the page's footer language switcher.

Please note, that you will be using the 3rd party plugin and the custom code at your own risk.

#1816261

Hi Jamal

Wow! Thank you so much. I really appreciate all your effort! 🙂

I think it is now clear that we cannot use Toolset/WPML out-of-the-box for favouriting, and I think that's a really important conclusion.

I played around a little with the site and did a few basic tests.

I made a new Page (My Favourites/Mein Merkzettel) and just plopped the [wp-favorite-posts] shortcode in, but that didn't work well, it just displays "Clear Favourites" but didn't display anything from any favourited posts.

(1) So, instead I created a new View my-favourite-job-ads-view, put it on the page My Favourites and filtered it using: field wpfp_favorites is a number equal to 1. Now I can get a list of favourited posts displayed and can use a Content Template. For some reason though, it is only working in German, not in English. In EN, all posts are being shown, regardless of what is favourited. Am I missing something obvious?

For the test, I created
- post ID 74 in EN >> post ID 75 (DE duplicate) was created automatically
- post ID 76 in DE >> post ID 77 (EN duplicate) was created automatically

(2) Are you able to see what is stored in that field wpfp_favorites on the database? Filtering on number =1 seems to display the list successfully, so I am presuming that it should go back to 0 when it is unfavourited, right? Or is it nulled again?

(3) Newly created Ads appear to be favourited by default. Is that intentional? I would expect newly created Posts to be in an unfavourited state until a user clicks the heart.

(4) I notice the plugin also offers the useful feature "Clear all favourites". Can I use [wpml-string] to translate plugin strings like "Clear Favourites"? I don't see any strings from the plugin popping up in WPML > String Translation, even after scanning all plugins etc within WPML.
(5) I created another user (role Subscriber ID 3, mckowski) with another email address, and viewed the site hidden link without even having to log in. This user can view everything without having to log in. Is it ensured that the favourites are user-specific?

Thanks and best regards
Simon

#1817475

Jamal
Supporter

Languages: English (English ) French (Français )

Timezone: Africa/Casablanca (GMT+01:00)

Hello Simon, Awesome, I think we're close to getting a working favorites system, integrating with how you are using WPML.

I think that [wp-favorite-posts] will not work well with WPML, because the plugin queries the database directly instead of using WordPress WP_Query API, which WPML hooks into.

The view to query favorited posts is probably the best way, but it seems that the plugin is saving favorited posts in a user meta instead of post meta, so the view should not have a query filter on any post meta. Otherwise, all users will be able to see all favorited posts.

Instead, we need to tell the view that it should query the database and return only the posts that are already saved in the user meta. So, we'll need a custom code to do so, I was able to come up with the following which is working nicely:

add_filter( 'wpv_filter_query', 'prefix_get_favorite_posts', 30, 3 );
 
function prefix_get_favorite_posts( $query_args, $view_settings, $view_id ) {
	if ( ($view_id == 69)  and function_exists( 'wpfp_get_user_meta' ) ){
		$query_args['post__in'] = wpfp_get_user_meta();
	}
	
    return $query_args;
}

Newly created ads are not favorited by default, we just used the wrong FontAwesome icon 🙂

To translate the plugin's strings such as "Clear Favourites", please follow this article https://wpml.org/documentation/getting-started-guide/string-translation/finding-strings-that-dont-appear-on-the-string-translation-page/

But, I don't think, you will be able to use the feature "Clear Favourites", as we won't be using the [wp-favorite-posts]
Instead, you will need to add the [wpfp-link] inside the view to let users unfavorite posts.