Package: contentratings
This package provides an infrastructure for adding ratings to Zopecontent.  It supports multiple categorized ratings per content objectand includes views for displaying those ratings.  For Plone ratingsupport, please see `plone.contentratings`.Detailed Documentation**********************===============Content Ratings===============This is a simple python package driven by Zope 3, which lets users(including un-authenticated users) rate content.  It provides a set ofinterfaces, adapters and views to allow the application of ratings toany IAnnotatable object.Dependencies:- BTrees- persistent- zope.annotations- zope.app.content- zope.app.testing- zope.component- zope.componentvocabulary- zope.container- zope.interface- zope.lifecycleevent- zope.location- zope.schema- zope.talesAll of these packages are included in Zope 2.12+ and Plone 4.0+.  Thispackage is tested with Plone 4.0-4.3.Using contentratings in Your Packages or Products=================================================First install it somewhere in your *python path* (not in your Productsdirectory), $INSTANCE_HOME/lib/python may be a good place to start using itwith zope.  It can be installed as an egg from the Python Cheeseshop (PyPI).You'll need to load the zcml for this package, so make sure that theconfigure.zcml for your application contains::<include package="contentratings" />If you want to allow some content to be rated you must mark it as*annotatable*.  This is because the Rating Storage is contained in anannotation on the content object.  The standard way to do this is toto add the following to your product's configure.zcml::<content class=".content.MyContentClass"><implementsinterface="zope.annotation.interfaces.IAttributeAnnotatable"/></content>Rating Categories=================This package provides an infrastructure for defining `RatingCategories`.  A `Rating Category` is an object implementing the`IRatingCategory` interface, specifying a `title`, `description` (forthe UI), `view_name` (how the rating should be rendered and managed inthe UI), TALES expressions which determine when ratings can be viewedor set (`read_expr` and `write_expr`), an `order` (for the UI), andfinally a `storage` (a factory which creates a persistentimplementation of a rating API to be stored in an annotation). All ofthese attributes except `title` are optional, with sensible defaultvalues provided.  Any object may have multiple Rating Categoriesapplied to it each registered with a unique `name`.The Default Categories----------------------This package's default configuration provides two rating categories.One for user ratings, and one for editorial ratings.  They areregistered for the marker interfaces `IUserRatable` and`IEditorRatable` respectively.  The TALES expressions used todetermine when they apply are designed to work with objects containedin a Zope 2 CMF application (primarily for backwards compatibilitywith older versions of `contentratings` which used direct permissionchecks). Unless they want to allow all users to set andread the ratings, other applications will need to define categories withcustom conditions.Let's demonstrate how these categories might be used.  We need tocreate some content and mark it with our marker interface::>>> from zope.container.sample import SampleContainer>>> content = SampleContainer()>>> from contentratings.interfaces import IUserRatable>>> from zope.interface import alsoProvides>>> alsoProvides(content, IUserRatable)Now we can adapt to the rating category using the IUserRating interface::>>> from contentratings.interfaces import IUserRating>>> adapted = IUserRating(content)>>> adapted.titleu'User Rating'>>> float(adapted.averageRating)0.0>>> rating = adapted.rate(7.0)>>> float(adapted.averageRating)7.0>>> adapted.numberOfRatings1>>> rating = adapted.rate(8.0, 'me')>>> float(adapted.averageRating)7.5>>> adapted.numberOfRatings2For more details on the IUserRating API see `tests/userstorage.txt`.Editorial ratings are applied similarly, but have a much simplerimplementation::>>> from contentratings.interfaces import IEditorRatable, IEditorialRating>>> alsoProvides(content, IEditorRatable)>>> adapted = IEditorialRating(content)>>> adapted.titleu'Editor Rating'>>> adapted.rating is NoneTrue>>> adapted.rating = 6.0>>> float(adapted.rating)6.0>>> adapted.rating = 8.0>>> float(adapted.rating)8.0See `tests/editorialstorage.txt` for details on how theIEditorialRating API works.Let's remove these markers now so that we can examine customcategories::>>> from zope.interface import noLongerProvides>>> noLongerProvides(content, IUserRatable)>>> noLongerProvides(content, IEditorRatable)>>> IUserRatable(content) # doctest: +ELLIPSISTraceback (most recent call last):...TypeError: ('Could not adapt', ...)>>> IEditorialRating(content) # doctest: +ELLIPSISTraceback (most recent call last):...TypeError: ('Could not adapt', ...)Custom Rating Categories------------------------There are two ways to create a new rating category, declarativelyusing ZCML, or programatically using the category factory directlyfrom python.  Let's look at the ZCML way first.  To make it work weneed to enable the zcml directive::>>> from zope.configuration import xmlconfig>>> import contentratings>>> context = xmlconfig.file('meta.zcml', contentratings)Loading the package configuration will do the above automatically.Now we register our rating category using the`contentratings:category` directive::>>> context = xmlconfig.string("""... <configure...    xmlns:contentratings="http://namespaces.plone.org/contentratings">...  <contentratings:category...      for="zope.container.sample.SampleContainer"...      title="My Rating Category"...      />... </configure>""", context=context)Here we have made use of all of the category default values.  As aresult we have registered a category which uses the default ZODBstorage and `IUserRating` API, with no restrictions on who can getand set ratings.  We can verify this easily since the categories aresimply adapters providing the rating interface provided by the(default) storage::>>> from contentratings.interfaces import IUserRating>>> from zope.container.sample import SampleContainer>>> content = SampleContainer()>>> adapted = IUserRating(content)>>> IUserRating.providedBy(adapted)True>>> adapted.context is contentTrue>>> adapted.titleu'My Rating Category'Note that because we provided no name in the configuration, theadapter was registered as the default (unnamed) adapter.  The name ofthe category is the name under which the adapter is registered and itis stored in the category's name attribute::>>> adapted.name''To provide multiple categories, just register them with unique names::>>> context = xmlconfig.string("""... <configure...    xmlns:contentratings="http://namespaces.plone.org/contentratings">...  <contentratings:category...      for="zope.container.sample.SampleContainer"...      title="My Other Rating Category"...      name="other"...      />... </configure>""", context=context)>>> from zope.component import getAdapter>>> adapted = getAdapter(content, IUserRating, name=u'other')>>> adapted.titleu'My Other Rating Category'>>> adapted.nameu'other'If we wanted to accomplish the same thing programatically, we couldinstantiate the factory directly and register it as an adpater::>>> from contentratings.category import RatingsCategoryFactory>>> category = RatingsCategoryFactory(title=u'Another Title', name=u'another')>>> from zope.component import provideAdapter>>> provideAdapter(category, adapts=(SampleContainer,), provides=IUserRating,...                name=u'another')>>> adapted = getAdapter(content, IUserRating, name=u'another')>>> adapted.titleu'Another Title'>>> adapted.nameu'another'This involves some redundancy, since the interface provided by thestorage has to be explicitly declared, and the category name has to beprovided twice.  Otherwise they are equivalent.Note that categories are adapters, and adapters may only be registered underthe same name for different interfaces/classes.  As usual, for a given name,the adapter registered for the most specific interface will be chosen.The Rating Manager==================When the adapter corresponding to a given rating category isqueried, the object returned is not actually a `Rating Category`itself, but a `Rating Manager`::>>> adapted # doctest: +ELLIPSIS<contentratings.category.RatingCategoryAdapter ...>>>> from contentratings.interfaces import IRatingManager>>> IRatingManager.providedBy(adapted)TrueThe Rating Manager provides the API of the storage, and also many ofthe attributes of the category.  It protects direct access to thestorage by checking the expressions specified for the category.  Themanager is implemented as a multi-adapter on the category and thecontext, but generally it should not be retrieved directly.  Thecategory adapter is responsible for retrieving it.  The manager isresponsible for setting up the category specific storage on thecontent object.>>> adapted.category # doctest: +ELLIPSIS<contentratings.category.RatingsCategoryFactory ...>>>> adapted.context # doctest: +ELLIPSIS<zope...container.sample.SampleContainer ...>>>> adapted.storage # doctest: +ELLIPSIS<contentratings.storage.UserRatingStorage ...>>>> isinstance(adapted.storage, adapted.category.storage)TrueSince the rating manager is responsible for security checks andpopulating the TALES expression context, it is very likely thatapplications will want to replace this component (locally or forspecific content) with a subclass to provide application specificsecurity.The Views=========The Rating Views----------------Each category has an associated `view_name` which is simply the nameof a view registered for the rating interface (e.g. IUserRating) to beused when rendering the category in the UI.  These are looked up onthe Rating Manager and have access to the `IRatingManager` API, aswell as the protected rating storage API, as provided by themanager (e.g. IUserRating).Resuable base classes for rating views are provided in`browser/basic.py` (`BasicUserRatingView` and`BasicEditorialRatingView`).  These views use a named vocabulary tovalidate input and use the IRatingManager API to determine who can andcannot rate content.  The package configuration provides a few ratingviews by default::ratings_view (default):  A rating using 1-5 large (25px) starssmall_stars: A rating using 1-5 small (10px) starsthree_small_stars: A rating using 1-3 small (10px) starsThese are each highly customizable using CSS. They are all registeredfor IUserRating.  Additionally, there is a `rating_view` is registeredfor IEditorialRating.The views are responsible for looking up a rating vocabulary andvalidating user input, as well as rendering the user interface.  Thesecurity is enforced by the `Rating Manager` used by the view, howeverthe view may go directly to the storage from rating manager if itwants to override the expression checking (e.g. showing a user theirown ratings, though they cannot see others).  Creating new views(e.g. non-starred ratings) is quite simple.A utility is provided for efficiently determining a reusable sessionkey in a generic manner.  This can be used to prevent repeat votingfrom anonymous users.  Applications which implement their ownanonymous session tracking mechanisms may override this utilitylocally if desired.The Aggregator Views--------------------There are also views which find all the rating categories available onthe content object being viewed, rendering them in order.  These areintended to be used within a viewlet, portlet, macro, or similar.  Theaggregator view for user ratings is called `user-ratings` and the onefor editorial ratings is called `editorial-ratings`.The Storage===========Though the `UserRatingStorage` should be sufficient fr most usecases,this package provides a simplemechanism for using custom objects forstoring ratings.  Two storage factory implementations are included,both of which use the ZODB for storing ratings: one implementing the`IUserRating` interface and the other the `IEditorialRating`interface.  The former is intended to be used for any content whichwill be rated (or voted on) by multiple users.  The latter stores asingle "editorial" rating on content, and exists primarily forbackwards compatibility.A custom sotrage factory (possibly sub-classed from one of the includedimplementations), can be specified using the `storage` attribute ofthe zcml directive, or `storage` parameter of the rating category factory.Not only are the storages replaceable, they can implement completelycustom APIs for managing ratings.  Though the need for this is isprobably limited, you may create a custom storage API by making aninterface for managing ratings, and having that interface provide`IRatingType`.  See the storage documentation in `tests/` for moreinformation.Why a New Rating Package========================There are already the `ATRatings`, `lovely.rating`, `iqpp.rating`, and`iqpp.plone.rating` packages, why do we need another package?First, contentratings preceeds all of those except ATRatings, which isuseful only under Plone with Archetypes content.  `contentratings` wasoriginally a very simple package intended to make it easy fordevelopers to add ratings to their products and applications.However, there appears to be much demand for an end-user product tofacilitate adding ratings to existing content objects.Unfortunately, none of these packages offer direct support formultiple ratings on a single content object, which appears to be acommon need.  It also (along with `lovely.rating`) decouples therating scoring system from the view in a manner which is probablyundesirable for a product which wants to allow user customization ofratings.  Changing these packages to support these use-cases wouldhave required a complete rewrite.  As a result, I rewrote the simplest(and most familiar) of these rating packages to support theseusecases, and also created a new package to integrate this newfunctionality for Plone end-users (see `plone.contentratings`).ToDo====* Provide view customization examples* Make the views work with Zope 3 authentication* Port the KSS view from plone.contentratings into contentratings (itcurrently depends on some plone KSS commands)Credits=======Author:-------* **Alec Mitchell** <apm13@columbia.edu>Contributors:-------------* **Maurizio Delmonte**(feel free to add your name above if you have made significant contributions)Thanks To:----------* **Geoff Davis** author of `ATRatings` from which icons and ideas werestolen.* **Philipp von Weitershausen** author of`Web Component Development with Zope 3`_ which provides a nice exampleof an annotation based rating product, which was the starting point forthis implementation.* **Kai Diefenbach** author of `iqpp.plone.rating` from which other iconsand UI ideas were stolen.* Some icons are from **Mark James'** `Silk icon set 1.3`_* The star rating is based on `CSS Star Rating Redux`_ via `iqpp.plone.rating`.. _`Web Component Development with Zope 3`: http://www.worldcookery.com/.. _`Silk icon set 1.3`: http://www.famfamfam.com/lab/icons/silk/.. _`CSS Star Rating Redux`: http://komodomedia.com/blog/index.php/2007/01/20/css-star-rating-redux/Changelog=========1.2.1 (2017-03-15)----------------- Fixed XSS vulnerability on rating action[keul, cekk]1.1 (2013-09-20)----------------* Update to support Zope 2.13 and Plone 4.0 through 4.3 and clear updependencies.1.0-final (2011-08-21)----------------------* Template markup fixes for chameleon compatibility.* Added Basque and Catalan translations.1.0-rc3 (2011-05-12)--------------------* Optimize for average/count calculations with large numbers ofratings.  Improve performance of most recent rating lookup.* Fixes for Plone 4.11.0-rc2 (2010-11-28)--------------------* Don't use a CSS id in view only rating view so that listings withratings can still have valid html.1.0-rc1 (2010-08-31)--------------------* Add support for view only rating views.* Remove _v attribute caching.* Plone 4 / Zope 2.12 compatibility (still works on Plone 3.3.4 / Zope 2.10.11):* permissions.py: Don't depend on Products.__ac_permissions__* Import ApplicationDefaultPermissions from AccessControl.Permission* Fix tests:* traverser.py, README.txt: SampleContainer class may comefrom zope.* and zope.app.** utils.py: Date may or may not be followed by semicolon* browser/basic.py: add publishTraverse method to BasicEditorialRatingView,to prevent faulty traversal.* events.py and interfaces.py: Subclass IObjectRatedEvent from IObjectEvent(instead of from IObjectModifiedEvent) to prevent permissions errors.* Added this file0.2 (2009-05-11)----------------