Best practice for "favorites"-like feature

I am designing a feature for a custom content type, where the user should be able to "Favorite" (or "like") a content item, and then, in a special view, they can se a list of all favorited items.

I thought about creating a field in the content type which would store a list of userids for users who favorited it. So, when the user clicks on the "Favorite this" action, the field would be updated and their username would be appended.

Then I considered storing the UID of the favorite objects on the user data (maybe as an user property).

What would be the more reasonable approach ?

Quaive has exactly this feature and is using a persistent tool to store and lookup this kind of relations:

You may be interested in picking just the bits you are interested into.
The code in the public repository is quite old and is missing some fixes, but it may help you not reinventing everything from scratch.

You could consider building an add-on on top of the collective.subscribe library, which stores human-to-content relations. This was built for collective.inviting (an RSVP add-on), but could be used for any principal-to-content relationship, where principal has a tuple signature of namespace, identifier (e.g. also supports non-authenticated users).

Sean

@alert a real shame they abandoned the original plonesocial approach of building plugins instead of a single code base that doesn't play nice with other plugins. Real loss to plone.

1 Like

At my former job I'd built something like that for our intranet almost entirely through the web... a portal_action to "pin" the current item, which would invoke a Script (Python) to add a Link object to the logged in user's home folder's "My Pins" subfolder; another portal_action to display the logged in user's pins; a portlet that displayed the logged in user's pins.

Hi, what about https://pypi.python.org/pypi/collective.favorites ?
I think it does exactly what you want.
It uses a OOBTree as an annotation on the portal, the key is the userid, the value is a PersistentList of dict.
Each dict contains the object UID, view name and query string.
The source is at https://github.com/collective/collective.favorites

I don't think it's a good idea to have a favorite field on your content type where you append userid. First, it's not enough, you may want to favorite a specific view of your content, not necessary /view, Second, how do you retrieve all the favorites of a user? I can't think of a effective way to do that.

1 Like

I kind of share your feelings but I understand why they did it; IIRC, the project managing and quality assurance process was quite difficult and was consuming a lot of resources of the Quaive team.

may be in the future they can go back to the old approach.

It's so much easier having one code base. With the number of customizations and features provided, you need to find ways to cut down the complexity. I completely understand why they did it and think it was the correct choice.

Thanks @hvelarde and @vangheem. Doing continuous integration and multi-package merge QA across a whole fleet of packages was killing us. Plus, we do not believe in the "generated UI" philosophy.

That said, we've taken a lot of care to make our code base modular and re-usable. You can install the ploneintranet.network backend without having to install the whole ploneintranet suite. That gives you a fully ajaxified backend you can talk to, and our frontend for that is dead simple and can easily be customized to suit another project.
https://docs.ploneintranet.org/development/components/network.html

We've even taken care to separate out dependencies for all our packages as extras dependencies in setup.py. Unfortunately this hits a bug in z3.autoinclude which means you'll have to buildout all ploneintranet dependencies at the python level - without having to actually install all of that as add-ons.
https://mail.zope.org/pipermail/zope-dev/2008-December/033781.html

@vangheem @gyst. I used the wrong word. A single "code base" is less of an issue but rather now allowing for a plugable UI, or not allowing for the existing plone pluggable UI to be used, or perhaps to improve the way plone's plugins work so as to make it work nicely on an intranet. The consequence would be that existing plugins work with the intranet (lots don't) and parts of the intranet work in normal plone sites (it's great that the network part does).

I understand why they did it. It simplifies a lot of things to treat the intranet as a single application, hide what you don't like in plone and not have to consider usecases of reuse outside of the intranet. That makes is a lot cheaper to build and that matters a lot. But it is a shame because when I looked at reusing the intranet codebase I couldn;t do much with it as other plugins I want to use with it didn't work so I couldn't integrate additional features easily. You only get what you are given. It loses a competitive advantage it could have had over other off the shelf intranet solutions. Conversely, if more features, like favourites, were useful in regular plone then they could perhaps be improved by others and lesson the development needed to those putting money into the intranet.

Our focus on a really seamless user experience precludes the use of a pluggable UI, since that would offer a disjoint UX almost by definition. I dare say this choice has made it possible for us to build a truly sleek user experience that would not be attainable as a Barceloneta variant. You cannot just buildout some eggs += foo bar grab bag of add-ons and expect a seamless integration to result from that.

Adding an existing Plone addon to Quaive then requires some frontend work - it needs design and some templating to make it fit with our UI paradigm, and maybe do some backend integration also. We're using existing add-ons where we can and we contribute back improvements wherever possible.

As to re-using our code base outside of ploneintranet: I've personally made a heavy investment in modular code architecture, extensive test coverage and documentation to try and make that happen. But since we don't produce generic add-ons you don't get generic add-ons. You get building blocks, like the liking backend, the social network, the view tracking, theme switching etc. It's more a toolbox than a retail shop. Anybody who wants to use pieces of that and runs into issues: please ping me. I'm also more than happy to backport bugfixes etc from our private tree to the public tree, as long as I know it's not a wasted effort and at least one somebody will use it.

But the fact remains: unlike Plone, Quaive is not supposed to be a mashup of add-ons. And yes that fundamental difference in approach robs us of synergies, which is a sad thing.

aw hell, I wish I'd known about that :slight_smile:

That's just one way to see it; the other is that Quaive gains a competitive advantage by being focused and not being burdened trying to do more.