backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 26 Apr 2010 18:48:28 +0200
changeset 202 b201437d2c20
parent 194 e19a103bf187 (current diff)
parent 201 4ebf5a6d1a54 (diff)
child 205 246ca4f3030e
backport stable
--- a/.hgtags	Thu Apr 15 19:01:55 2010 +0200
+++ b/.hgtags	Mon Apr 26 18:48:28 2010 +0200
@@ -14,3 +14,5 @@
 2d7395dcc266985522d49a94786d525d19a6e535 cubicweb-blog-debian-version-1.7.1-1
 97d3cac5c4bc0e9b2fd8a09e4b728ef9bb100a2f cubicweb-blog-version-1.7.3
 51ec7229996d86fd006712258ffe121b5205478b cubicweb-blog-debian-version-1.7.3-1
+67460215a89e547c9d388c47c50f9f43fae6758f cubicweb-blog-version-1.7.4
+fd1d9f3a0b928870eff77dd521d0e8dc2384bd6d cubicweb-blog-debian-version-1.7.4-1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Mon Apr 26 18:48:28 2010 +0200
@@ -0,0 +1,42 @@
+Summary
+-------
+The `blog` cube provides blogging functionnalities. It creates two entity types,
+`Blog` and `BlogEntry`. There are related to each other by the relation
+`BlogEntry entry_of Blog`.
+
+Usage
+-----
+
+When a user submits a blog entry, it goes in a `draft` state until the blog
+entry is published by an application managers. The blog entry will not be
+visible until it reaches the `published` state.
+
+When a blog entry is submitted, an email notification is automatically sent
+to all the users belonging to the `managers` group of the application.
+
+Specific boxes provided by this cube:
+
+- `BlogEntryArchiveBox`, displays a box with the total number of blog entries
+  submitted by month for the last twelve months.
+
+- `BlogEntryListBox`, displays a box with the latest five blog entries
+  published in your application as well as link to subscribe to a RSS feed.
+
+- `BlogEntrySummary`, displays a box with the list of users who submitted
+  blog entries and the total number of blog entries they submitted.
+
+This cube also provides some web services such as:
+
+- http://xx:xxxx/blogentries/YYYY to retrieve the blog entries submitted
+  during the year YYYY through a RSS feed
+
+- http://xx:xxxx/blogentries/YYYY/MM to retrieve the blog entries submitted
+  during the month MM of the year YYYY through a RSS feed
+
+- http://xx:xxxx/blog/[eid]/blogentries/YYYY to retrieve the blog entries
+  submitted in the blog of identifier [eid], during the year YYYY through
+  a RSS feed
+
+- http://xx:xxxx/blog/[eid]/blogentries/YYYY/MM to retrieve the blog entries
+  submitted in the blog of identifier [eid], during the month MM of the
+  year YYYY through a RSS feed
--- a/__pkginfo__.py	Thu Apr 15 19:01:55 2010 +0200
+++ b/__pkginfo__.py	Mon Apr 26 18:48:28 2010 +0200
@@ -4,7 +4,7 @@
 modname = 'blog'
 distname = "cubicweb-%s" % modname
 
-numversion = (1, 7, 3)
+numversion = (1, 7, 4)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL'
@@ -13,51 +13,8 @@
 author_email = "contact@logilab.fr"
 web = 'http://www.cubicweb.org/project/%s' % distname
 
-short_desc = "blogging component for the CubicWeb framework"
-long_desc = """\
-Summary
--------
-The `blog` cube provides blogging functionnalities. It creates two entity types,
-`Blog` and `BlogEntry`. There are related to each other by the relation
-`BlogEntry entry_of Blog`.
-
-Usage
------
-
-When a user submits a blog entry, it goes in a `draft` state until the blog
-entry is published by an application managers. The blog entry will not be
-visible until it reaches the `published` state.
-
-When a blog entry is submitted, an email notification is automatically sent
-to all the users belonging to the `managers` group of the application.
-
-Specific boxes provided by this cube:
-
-- `BlogEntryArchiveBox`, displays a box with the total number of blog entries
-  submitted by month for the last twelve months.
-
-- `BlogEntryListBox`, displays a box with the latest five blog entries
-  published in your application as well as link to subscribe to a RSS feed.
-
-- `BlogEntrySummary`, displays a box with the list of users who submitted
-  blog entries and the total number of blog entries they submitted.
-
-This cube also provides some web services such as:
-
-- http://xx:xxxx/blogentries/YYYY to retrieve the blog entries submitted
-  during the year YYYY through a RSS feed
-
-- http://xx:xxxx/blogentries/YYYY/MM to retrieve the blog entries submitted
-  during the month MM of the year YYYY through a RSS feed
-
-- http://xx:xxxx/blog/[eid]/blogentries/YYYY to retrieve the blog entries
-  submitted in the blog of identifier [eid], during the year YYYY through
-  a RSS feed
-
-- http://xx:xxxx/blog/[eid]/blogentries/YYYY/MM to retrieve the blog entries
-  submitted in the blog of identifier [eid], during the month MM of the
-  year YYYY through a RSS feed
-"""
+description = "blogging component for the CubicWeb framework"
+short_desc = description # XXX cw < 3.8 bw compat
 
 classifiers = [
     'Environment :: Web Environment'
@@ -66,9 +23,13 @@
     'Programming Language :: JavaScript',
     ]
 
-__depends_cubes__ = {}
 __depends__ = {'cubicweb': '>= 3.7.3'}
-__use__ = tuple(__depends_cubes__)
+__recommends_cubes__ = {'tag': None,
+                        'comment': '>= 1.6.3'}
+__recommends__ = {}
+for cube in __recommends_cubes__:
+    __recommends__['cubicweb-'+cube] = __recommends_cubes__[cube]
+
 
 # package ###
 
--- a/debian/changelog	Thu Apr 15 19:01:55 2010 +0200
+++ b/debian/changelog	Mon Apr 26 18:48:28 2010 +0200
@@ -1,3 +1,9 @@
+cubicweb-blog (1.7.4-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Mon, 26 Apr 2010 15:22:14 +0200
+
 cubicweb-blog (1.7.3-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Thu Apr 15 19:01:55 2010 +0200
+++ b/debian/control	Mon Apr 26 18:48:28 2010 +0200
@@ -10,6 +10,7 @@
 Package: cubicweb-blog
 Architecture: all
 Depends: cubicweb-common (>= 3.7.3)
+Suggests: cubicweb-comment (>= 1.6.3), cubicweb-tag
 Description: blog component for the CubicWeb framework
  This CubicWeb component provides blogging functionnalities.
  .
--- a/debian/rules	Thu Apr 15 19:01:55 2010 +0200
+++ b/debian/rules	Mon Apr 26 18:48:28 2010 +0200
@@ -7,7 +7,7 @@
 build: build-stamp
 build-stamp:
 	dh_testdir
-	python setup.py -q build
+	NO_SETUPTOOLS=1 python setup.py -q build
 	touch build-stamp
 
 clean:
@@ -24,7 +24,7 @@
 	dh_testroot
 	dh_clean -k
 	dh_installdirs -i
-	python setup.py -q install --no-compile --prefix=debian/cubicweb-blog/usr/
+	NO_SETUPTOOLS=1 python setup.py -q install --no-compile --prefix=debian/cubicweb-blog/usr/
 	rm -rf debian/cubicweb-blog/usr/lib/python*
 
 
--- a/i18n/en.po	Thu Apr 15 19:01:55 2010 +0200
+++ b/i18n/en.po	Mon Apr 26 18:48:28 2010 +0200
@@ -52,9 +52,6 @@
 msgid "blog's rss url (useful for when using external site such as feedburner)"
 msgstr ""
 
-msgid "blog_latest_box"
-msgstr "latest blogs"
-
 msgid "blogged in "
 msgstr ""
 
@@ -67,18 +64,20 @@
 msgid "boxes_blog_archives_box_description"
 msgstr "box displaying latest blog entries posted"
 
-msgid "boxes_blog_latest_box"
-msgstr "box displaying latest blog entries posted"
+msgid "boxes_blog_summary_box"
+msgstr "posts by author"
+
+msgid "boxes_blog_summary_box_description"
+msgstr ""
+"this box contains a list of authors with the number of blog entries they "
+"posted"
+
+msgid "boxes_latest_blogs_box"
+msgstr "latest blogs"
 
 # add related box generated message
-msgid "boxes_blog_latest_box_description"
-msgstr "this box contains the latest posts"
-
-msgid "boxes_blog_summary_box"
-msgstr "Posts by author"
-
-msgid "boxes_blog_summary_box_description"
-msgstr "this box contains a list of authors with the number of posts"
+msgid "boxes_latest_blogs_box_description"
+msgstr "this box contains the latest blog entries posted"
 
 msgid "by"
 msgstr "by"
@@ -97,6 +96,12 @@
 
 msgctxt "BlogEntry"
 msgid "content_format"
+msgstr "format"
+
+msgid "contentnavigation_blogsubscribe"
+msgstr "blog subscribtion icon"
+
+msgid "contentnavigation_blogsubscribe_description"
 msgstr ""
 
 msgid "creating BlogEntry (BlogEntry entry_of Blog %(linkto)s)"
@@ -111,7 +116,7 @@
 
 msgctxt "Blog"
 msgid "description_format"
-msgstr ""
+msgstr "format"
 
 msgid "draft"
 msgstr ""
@@ -123,14 +128,20 @@
 
 msgctxt "BlogEntry"
 msgid "entry_of"
-msgstr ""
+msgstr "blog entry of"
 
 msgctxt "Blog"
 msgid "entry_of_object"
-msgstr ""
+msgstr "posts"
 
 msgid "entry_of_object"
-msgstr "contains"
+msgstr "posts"
+
+msgid "latest_blogs_blog_box"
+msgstr "latest blogs"
+
+msgid "latest_blogs_box"
+msgstr "latest blogs"
 
 msgid "next month"
 msgstr ""
@@ -155,14 +166,20 @@
 
 msgctxt "Blog"
 msgid "rss_url"
-msgstr ""
+msgstr "rss feed url"
 
 msgid "see more"
 msgstr "see more"
 
+msgid "see more archives"
+msgstr ""
+
 msgid "subscribe"
 msgstr "subscribe"
 
+msgid "subscribe to this blog"
+msgstr ""
+
 msgid "tags"
 msgstr "tags"
 
@@ -173,18 +190,3 @@
 msgctxt "BlogEntry"
 msgid "title"
 msgstr ""
-
-#~ msgid "add a Blog"
-#~ msgstr "add a blog"
-
-#~ msgid "add a BlogEntry"
-#~ msgstr "add a blog entry"
-
-#~ msgid "more"
-#~ msgstr "more"
-
-#~ msgid "remove this Blog"
-#~ msgstr "remove this blog"
-
-#~ msgid "remove this BlogEntry"
-#~ msgstr "remove this blog entry"
--- a/i18n/es.po	Thu Apr 15 19:01:55 2010 +0200
+++ b/i18n/es.po	Mon Apr 26 18:48:28 2010 +0200
@@ -51,9 +51,6 @@
 msgid "blog's rss url (useful for when using external site such as feedburner)"
 msgstr ""
 
-msgid "blog_latest_box"
-msgstr "últimos blogs"
-
 msgid "blogged in "
 msgstr ""
 
@@ -63,18 +60,18 @@
 msgid "boxes_blog_archives_box_description"
 msgstr "espacio hacia los archivos del blog en meses anteriores"
 
-msgid "boxes_blog_latest_box"
-msgstr "último"
-
-msgid "boxes_blog_latest_box_description"
-msgstr "espacio que contiene la lista de los últimos blogs"
-
 msgid "boxes_blog_summary_box"
 msgstr ""
 
 msgid "boxes_blog_summary_box_description"
 msgstr ""
 
+msgid "boxes_latest_blogs_box"
+msgstr "último"
+
+msgid "boxes_latest_blogs_box_description"
+msgstr "espacio que contiene la lista de los últimos blogs"
+
 msgid "by"
 msgstr "por"
 
@@ -94,6 +91,12 @@
 msgid "content_format"
 msgstr ""
 
+msgid "contentnavigation_blogsubscribe"
+msgstr ""
+
+msgid "contentnavigation_blogsubscribe_description"
+msgstr ""
+
 msgid "creating BlogEntry (BlogEntry entry_of Blog %(linkto)s)"
 msgstr "creación de una entrada de blog %(linkto)s"
 
@@ -127,6 +130,12 @@
 msgid "entry_of_object"
 msgstr "contiene las entradas"
 
+msgid "latest_blogs_blog_box"
+msgstr ""
+
+msgid "latest_blogs_box"
+msgstr "últimos blogs"
+
 msgid "next month"
 msgstr ""
 
@@ -155,9 +164,15 @@
 msgid "see more"
 msgstr "ver más"
 
+msgid "see more archives"
+msgstr ""
+
 msgid "subscribe"
 msgstr "suscribirse"
 
+msgid "subscribe to this blog"
+msgstr ""
+
 msgid "tags"
 msgstr "palabras clave"
 
--- a/i18n/fr.po	Thu Apr 15 19:01:55 2010 +0200
+++ b/i18n/fr.po	Mon Apr 26 18:48:28 2010 +0200
@@ -33,7 +33,7 @@
 msgstr "Nouveau blog"
 
 msgid "New BlogEntry"
-msgstr "Nouveau  billet"
+msgstr "Nouveau billet"
 
 msgid "This Blog"
 msgstr "Ce blog"
@@ -47,21 +47,18 @@
 
 #, python-format
 msgid "blog entries created by %s"
-msgstr ""
+msgstr "billets créés par %s"
 
 msgid "blog's rss url (useful for when using external site such as feedburner)"
 msgstr ""
 "url du flux rss du blog (peut être utile lorsque l'on utilise un service tel "
 "que feedburner)"
 
-msgid "blog_latest_box"
-msgstr "derniers blogs"
-
 msgid "blogged in "
 msgstr "bloggué dans "
 
 msgid "boxes_blog_archives_box"
-msgstr "boîte d'archive des blogs"
+msgstr "archive des blogs"
 
 # subject and object forms for each relation type
 # (no object form for final relation types)
@@ -69,18 +66,18 @@
 msgid "boxes_blog_archives_box_description"
 msgstr "boîte permettant d'accéder aux archives des blogs pour les mois passés"
 
-msgid "boxes_blog_latest_box"
-msgstr "dernier"
+msgid "boxes_blog_summary_box"
+msgstr "billets par auteur"
+
+msgid "boxes_blog_summary_box_description"
+msgstr "boîte contenant la liste des auteurs et le nombre de billets"
+
+msgid "boxes_latest_blogs_box"
+msgstr "derniers billets"
 
 # add related box generated message
-msgid "boxes_blog_latest_box_description"
-msgstr "boîte contentant la liste des derniers blogs"
-
-msgid "boxes_blog_summary_box"
-msgstr "blogs par auteur"
-
-msgid "boxes_blog_summary_box_description"
-msgstr "boîte contenant la liste des auteurs et le nombre de blogs"
+msgid "boxes_latest_blogs_box_description"
+msgstr "boîte contentant la liste des derniers billets postés"
 
 msgid "by"
 msgstr "par"
@@ -88,32 +85,38 @@
 # subject and object forms for each relation type
 # (no object form for final relation types)
 msgid "content"
-msgstr ""
+msgstr "contenu"
 
 msgctxt "BlogEntry"
 msgid "content"
-msgstr ""
+msgstr "contenu"
 
 msgid "content_format"
-msgstr ""
+msgstr "format"
 
 msgctxt "BlogEntry"
 msgid "content_format"
-msgstr ""
+msgstr "format"
+
+msgid "contentnavigation_blogsubscribe"
+msgstr "souscrire au blog"
+
+msgid "contentnavigation_blogsubscribe_description"
+msgstr "icône permettant de souscrire à un blog"
 
 msgid "creating BlogEntry (BlogEntry entry_of Blog %(linkto)s)"
 msgstr "création d'un billet dans le blog %(linkto)s"
 
 msgid "default BlogEntry workflow"
-msgstr ""
+msgstr "workflow des billets par défaut"
 
 msgctxt "Blog"
 msgid "description"
-msgstr ""
+msgstr "description"
 
 msgctxt "Blog"
 msgid "description_format"
-msgstr ""
+msgstr "format"
 
 msgid "draft"
 msgstr "brouillon"
@@ -125,14 +128,20 @@
 
 msgctxt "BlogEntry"
 msgid "entry_of"
-msgstr ""
+msgstr "dans le blog"
 
 msgctxt "Blog"
 msgid "entry_of_object"
-msgstr ""
+msgstr "billets"
 
 msgid "entry_of_object"
-msgstr "contient les entrées"
+msgstr "billets"
+
+msgid "latest_blogs_blog_box"
+msgstr "derniers billets dans ce blog"
+
+msgid "latest_blogs_box"
+msgstr "derniers billets"
 
 msgid "next month"
 msgstr "mois suivant"
@@ -157,36 +166,27 @@
 
 msgctxt "Blog"
 msgid "rss_url"
-msgstr ""
+msgstr "url du flux RSS"
 
 msgid "see more"
 msgstr "voir plus"
 
+msgid "see more archives"
+msgstr "voir plus d'archives"
+
 msgid "subscribe"
 msgstr "souscrire"
 
+msgid "subscribe to this blog"
+msgstr "souscrire à ce blog"
+
 msgid "tags"
 msgstr "étiquettes"
 
 msgctxt "Blog"
 msgid "title"
-msgstr ""
+msgstr "titre"
 
 msgctxt "BlogEntry"
 msgid "title"
-msgstr ""
-
-#~ msgid "add a Blog"
-#~ msgstr "ajouter un blog"
-
-#~ msgid "add a BlogEntry"
-#~ msgstr "ajouter un billet"
-
-#~ msgid "blog entries created by %s %s"
-#~ msgstr "billets créés par %s %s"
-
-#~ msgid "remove this Blog"
-#~ msgstr "supprimer ce blog"
-
-#~ msgid "remove this BlogEntry"
-#~ msgstr "supprimer cette entrée de blog"
+msgstr "titre"
--- a/setup.py	Thu Apr 15 19:01:55 2010 +0200
+++ b/setup.py	Mon Apr 26 18:48:28 2010 +0200
@@ -1,50 +1,170 @@
 #!/usr/bin/env python
 # pylint: disable-msg=W0404,W0622,W0704,W0613,W0152
-# Copyright (c) 2003-2004 LOGILAB S.A. (Paris, FRANCE).
-# http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Generic Setup script, takes package info from __pkginfo__.py file """
+"""Generic Setup script, takes package info from __pkginfo__.py file.
+
+:copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+import os
+import sys
+import shutil
+from os.path import isdir, exists, join, walk
 
-from distutils.core import setup
+try:
+    if os.environ.get('NO_SETUPTOOLS'):
+        raise ImportError()
+    from setuptools import setup
+    from setuptools.command import install_lib
+    USE_SETUPTOOLS = 1
+except ImportError:
+    from distutils.core import setup
+    from distutils.command import install_lib
+    USE_SETUPTOOLS = 0
 
+
+sys.modules.pop('__pkginfo__', None)
 # import required features
-from __pkginfo__ import distname, version, license, short_desc, long_desc, \
+from __pkginfo__ import modname, version, license, description, \
      web, author, author_email
 # import optional features
-try:
-    from __pkginfo__ import data_files
-except ImportError:
-    data_files = None
-try:
-    from __pkginfo__ import include_dirs
-except ImportError:
-    include_dirs = []
+import __pkginfo__
+distname = getattr(__pkginfo__, 'distname', modname)
+scripts = getattr(__pkginfo__, 'scripts', [])
+data_files = getattr(__pkginfo__, 'data_files', None)
+include_dirs = getattr(__pkginfo__, 'include_dirs', [])
+ext_modules = getattr(__pkginfo__, 'ext_modules', None)
+dependency_links = getattr(__pkginfo__, 'dependency_links', [])
+
+STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build')
+
+IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~')
+
+if exists('README'):
+    long_description = file('README').read()
+else:
+    long_description = ''
+if USE_SETUPTOOLS:
+   requires = {}
+   for entry in ("__depends__", "__recommends__"):
+      requires.update(getattr(__pkginfo__, entry, {}))
+   install_requires = [("%s %s" % (d, v and v or "")).strip()
+                       for d, v in requires.iteritems()]
+else:
+   install_requires = []
+
+
+def ensure_scripts(linux_scripts):
+    """Creates the proper script names required for each platform
+    (taken from 4Suite)
+    """
+    from distutils import util
+    if util.get_platform()[:3] == 'win':
+        scripts_ = [script + '.bat' for script in linux_scripts]
+    else:
+        scripts_ = linux_scripts
+    return scripts_
+
+def get_packages(directory, prefix):
+    """return a list of subpackages for the given directory"""
+    result = []
+    for package in os.listdir(directory):
+        absfile = join(directory, package)
+        if isdir(absfile):
+            if exists(join(absfile, '__init__.py')) or \
+                   package in ('test', 'tests'):
+                if prefix:
+                    result.append('%s.%s' % (prefix, package))
+                else:
+                    result.append(package)
+                result += get_packages(absfile, result[-1])
+    return result
+
+def export(from_dir, to_dir,
+           blacklist=STD_BLACKLIST,
+           ignore_ext=IGNORED_EXTENSIONS,
+           verbose=True):
+    """make a mirror of from_dir in to_dir, omitting directories and files
+    listed in the black list
+    """
+    def make_mirror(arg, directory, fnames):
+        """walk handler"""
+        for norecurs in blacklist:
+            try:
+                fnames.remove(norecurs)
+            except ValueError:
+                pass
+        for filename in fnames:
+            # don't include binary files
+            if filename[-4:] in ignore_ext:
+                continue
+            if filename[-1] == '~':
+                continue
+            src = join(directory, filename)
+            dest = to_dir + src[len(from_dir):]
+            if verbose:
+                print >> sys.stderr, src, '->', dest
+            if os.path.isdir(src):
+                if not exists(dest):
+                    os.mkdir(dest)
+            else:
+                if exists(dest):
+                    os.remove(dest)
+                shutil.copy2(src, dest)
+    try:
+        os.mkdir(to_dir)
+    except OSError, ex:
+        # file exists ?
+        import errno
+        if ex.errno != errno.EEXIST:
+            raise
+    walk(from_dir, make_mirror, None)
+
+
+class MyInstallLib(install_lib.install_lib):
+    """extend install_lib command to handle  package __init__.py and
+    include_dirs variable if necessary
+    """
+    def run(self):
+        """overridden from install_lib class"""
+        install_lib.install_lib.run(self)
+        # manually install included directories if any
+        if include_dirs:
+            base = modname
+            for directory in include_dirs:
+                dest = join(self.install_dir, base, directory)
+                export(directory, dest, verbose=False)
 
 def install(**kwargs):
     """setup entry point"""
-    #kwargs['distname'] = modname
-    return setup(name=distname,
-                 version=version,
-                 license=license,
-                 description=short_desc,
-                 long_description=long_desc,
-                 author=author,
-                 author_email=author_email,
-                 url=web,
-                 data_files=data_files,
-                 **kwargs)
-            
+    if USE_SETUPTOOLS:
+        if '--force-manifest' in sys.argv:
+            sys.argv.remove('--force-manifest')
+    # install-layout option was introduced in 2.5.3-1~exp1
+    elif sys.version_info < (2, 5, 4) and '--install-layout=deb' in sys.argv:
+        sys.argv.remove('--install-layout=deb')
+    kwargs['package_dir'] = {modname : '.'}
+    packages = [modname] + get_packages(os.getcwd(), modname)
+    if USE_SETUPTOOLS and install_requires:
+        kwargs['install_requires'] = install_requires
+        kwargs['dependency_links'] = dependency_links
+    kwargs['packages'] = packages
+    return setup(name = distname,
+                 version = version,
+                 license = license,
+                 description = description,
+                 long_description = long_description,
+                 author = author,
+                 author_email = author_email,
+                 url = web,
+                 scripts = ensure_scripts(scripts),
+                 data_files = data_files,
+                 ext_modules = ext_modules,
+                 cmdclass = {'install_lib': MyInstallLib},
+                 **kwargs
+                 )
+
 if __name__ == '__main__' :
     install()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/views/boxes.py	Mon Apr 26 18:48:28 2010 +0200
@@ -0,0 +1,101 @@
+"""Various blog boxes: archive, per author, etc...
+
+:organization: Logilab
+:copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+_ = unicode
+
+from logilab.mtconverter import xml_escape
+
+from cubicweb.selectors import one_line_rset, implements
+from cubicweb.web.htmlwidgets import BoxLink, BoxWidget
+from cubicweb.web.views import boxes
+
+class BlogArchivesBox(boxes.BoxTemplate):
+    """blog side box displaying a Blog Archive"""
+    __regid__ = 'blog_archives_box'
+    title = _('boxes_blog_archives_box')
+    order = 35
+
+    def call(self, **kwargs):
+        """display blogs archive"""
+        # XXX turn into a selector
+        count_blogentry = self._cw.execute('Any COUNT(B) WHERE B is BlogEntry')
+        if count_blogentry[0][0] > 0:
+            box = BoxWidget(self._cw._(self.title), id=self.__regid__, islist=False)
+            box.append(boxes.BoxHtml(self._cw.view('blog_archive', None, maxentries=12)))
+            box.render(self.w)
+
+
+class BlogsByAuthorBox(boxes.BoxTemplate):
+    __regid__ = 'blog_summary_box'
+    title = _('boxes_blog_summary_box')
+    order = 36
+
+    def call(self, view=None, **kwargs):
+        box = BoxWidget(self._cw._(self.title), self.__regid__, islist=True)
+        rql = 'Any U, COUNT(B) GROUPBY U WHERE U is CWUser, ' \
+              'B is BlogEntry, B created_by U'
+        rset = self._cw.execute(rql)
+        for user in rset:
+            euser = self._cw.entity_from_eid(user[0])
+            box.append(BoxLink(self._cw.build_url('blogentry/%s' % euser.login),
+                                              u'%s [%s]' % (euser.name(),
+                                                            user[1])))
+        box.render(self.w)
+
+
+class LatestBlogsBox(boxes.BoxTemplate):
+    """display a box with latest blogs and rss"""
+    __regid__ = 'latest_blogs_box'
+    title = _('latest_blogs_box')
+    visible = True # enabled by default
+    order = 34
+    display_see_more_link = True
+
+    def latest_blogs_rset(self):
+        return self._cw.execute(
+            'Any X,T,CD ORDERBY CD DESC LIMIT 5 WHERE X is BlogEntry, '
+            'X title T, X creation_date CD')
+
+    def call(self, **kwargs):
+        # XXX turn into a selector
+        rset = self.latest_blogs_rset()
+        if not rset:
+            return
+        box = BoxWidget(self._cw._(self.title), self.__regid__, islist=True)
+        # TODO - get the date between brakets after link
+        # empty string for title argument to deactivate auto-title
+        for i in xrange(rset.rowcount):
+            entity = rset.get_entity(i, 0)
+            box.append(BoxLink(entity.absolute_url(), xml_escape(entity.dc_title())))
+        rqlst = rset.syntax_tree()
+        rqlst.set_limit(None)
+        rql = rqlst.as_string(kwargs=rset.args)
+        if self.display_see_more_link:
+            url = self._cw.build_url('view', rql=rql, page_size=10)
+            box.append(BoxLink(url,  u'[%s]' % self._cw._(u'see more')))
+        rss_icon = self._cw.external_resource('RSS_LOGO_16')
+        # FIXME - could use rss_url defined as a property if available
+        rss_label = u'%s <img src="%s" alt="%s"/>' % (
+            self._cw._(u'subscribe'), rss_icon, self._cw._('rss icon'))
+        rss_url = self._cw.build_url('view', vid='rss', rql=rql)
+        box.append(BoxLink(rss_url, rss_label))
+        box.render(self.w)
+
+
+class LatestBlogsBlogBox(LatestBlogsBox):
+    """display a box with latest blogs and rss, filtered for a particular blog
+    """
+    __select__ = LatestBlogsBox.__select__ & one_line_rset() & implements('Blog')
+    title = _('latest_blogs_blog_box')
+    display_see_more_link = False
+
+    def latest_blogs_rset(self):
+        blog = self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0)
+        return self._cw.execute(
+            'Any X,T,CD ORDERBY CD DESC LIMIT 5 WHERE X is BlogEntry, '
+            'X title T, X creation_date CD, X entry_of B, B eid %(b)s',
+            {'b': blog.eid})
--- a/views/primary.py	Thu Apr 15 19:01:55 2010 +0200
+++ b/views/primary.py	Mon Apr 26 18:48:28 2010 +0200
@@ -6,55 +6,71 @@
 """
 __docformat__ = "restructuredtext en"
 
-
 from logilab.mtconverter import xml_escape
 
 from cubicweb.utils import UStringIO
 from cubicweb.selectors import implements
-from cubicweb.web import uicfg
-from cubicweb.web.views import primary
+from cubicweb.web import uicfg, component
+from cubicweb.web.views import primary, workflow
 
-uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden')
-uicfg.primaryview_section.tag_attribute(('Blog', 'rss_url'), 'hidden')
-uicfg.primaryview_section.tag_attribute(('BlogEntry', 'title'), 'hidden')
-uicfg.primaryview_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden')
-uicfg.primaryview_section.tag_subject_of(('BlogEntry', 'entry_of', '*'),
-                                         'relations')
+_pvs = uicfg.primaryview_section
+_pvs.tag_attribute(('Blog', 'title'), 'hidden')
+_pvs.tag_attribute(('Blog', 'rss_url'), 'hidden')
+_pvs.tag_attribute(('BlogEntry', 'title'), 'hidden')
+_pvs.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden')
+_pvs.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations')
 
-uicfg.actionbox_appearsin_addmenu.tag_object_of(('*', 'entry_of', 'Blog'), True)
+_pvdc = uicfg.primaryview_display_ctrl
+_pvdc.tag_attribute(('Blog', 'description'), {'showlabel': False})
+
+_abaa = uicfg.actionbox_appearsin_addmenu
+_abaa.tag_object_of(('*', 'entry_of', 'Blog'), True)
+
+_afs = uicfg.autoform_section
+_afs.tag_subject_of(('BlogEntry', 'entry_of', 'Blog'), 'main', 'attributes')
 
 
 class BlogPrimaryView(primary.PrimaryView):
     __select__ = implements('Blog')
 
-    def render_entity_attributes(self, entity):
-        super(BlogPrimaryView, self).render_entity_attributes(entity)
-        self.w('<a class="right" href="%s">%s <img src="%s" alt="%s"/></a>' % (
-            xml_escape(entity.rss_feed_url()), self._cw._(u'subscribe'),
-            self._cw.external_resource('RSS_LOGO_16'), self._cw._('rss icon')))
-
     def render_entity_relations(self, entity):
         super(BlogPrimaryView, self).render_entity_relations(entity)
         rset = entity.related('entry_of', 'object')
         if rset:
             strio = UStringIO()
             self.paginate(self._cw, w=strio.write, page_size=10, rset=rset)
-            self.wview('sameetypelist', rset)
+            self.w(strio.getvalue())
+            self.wview('sameetypelist', rset, showtitle=False)
             self.w(strio.getvalue())
 
-    def render_entity_title(self, entity):
-        self.w(u'<h1>%s</h1>' % xml_escape(entity.dc_title()))
+
+class SubscribeToBlogComponent(component.EntityVComponent):
+    __regid__ = 'blogsubscribe'
+    __select__ = component.EntityVComponent.__select__ & implements('Blog')
+    context = 'ctxtoolbar'
+
+    def cell_call(self, row, col, view):
+        entity = self.cw_rset.get_entity(row, col)
+        self.w('<a href="%s"><img src="%s" alt="%s"/></a>' % (
+            xml_escape(entity.rss_feed_url()),
+            self._cw.external_resource('RSS_LOGO_16'),
+            self._cw._(u'subscribe to this blog')))
 
 
 class BlogEntryPrimaryView(primary.PrimaryView):
     __select__ = implements('BlogEntry')
     show_attr_label = False
 
-    def render_entity_title(self, entity):
-        self.w(u'<h1>%s</h1>' % xml_escape(entity.dc_title()))
-
     def render_entity_relations(self, entity):
         rset = entity.related('entry_of', 'subject')
         if rset:
             self.w(self._cw._('blogged in '))
             self.wview('csv', rset, 'null')
+
+
+# don't show workflow history for blog entry
+class BlogEntryWFHistoryVComponent(workflow.WFHistoryVComponent):
+    __select__ = workflow.WFHistoryVComponent.__select__ & implements('BlogEntry')
+
+    def cell_call(self, row, col, view=None):
+        pass
--- a/views/secondary.py	Thu Apr 15 19:01:55 2010 +0200
+++ b/views/secondary.py	Mon Apr 26 18:48:28 2010 +0200
@@ -13,10 +13,8 @@
 
 from cubicweb.schema import display_name
 from cubicweb.view import EntityView, StartupView
-from cubicweb.selectors import paginated_rset, sorted_rset, implements, \
-                               authenticated_user
-from cubicweb.web.htmlwidgets import BoxLink, BoxWidget
-from cubicweb.web.views import baseviews, boxes, calendar, navigation, workflow
+from cubicweb.selectors import paginated_rset, sorted_rset, implements
+from cubicweb.web.views import baseviews, calendar, navigation
 
 class BlogEntryArchiveView(StartupView):
     """control the view of a blog archive"""
@@ -36,15 +34,16 @@
         label = u'%s %s [%s]' % (self._cw._(calendar.MONTHNAMES[month-1]), year,
                                  nmb_entries)
         vtitle = '%s %s' % (display_name(self._cw, 'BlogEntry', 'plural'), label)
-        url = xml_escape(self._cw.build_url('view', rql=rql, month=month, year=year, vtitle=vtitle))
+        url = xml_escape(self._cw.build_url('view', rql=rql, month=month,
+                                            year=year, vtitle=vtitle))
         link = u'<a href="%s" title="">%s</a>' % (url, label)
         items.append( u'<li class="">%s</li>\n' % link )
 
     def call(self, maxentries=None, **kwargs):
         """display a list of entities by calling their <item_vid> view
         """
-        rset = self._cw.execute('Any CD ORDERBY CD DESC WHERE B is BlogEntry, B creation_date CD')
-
+        rset = self._cw.execute('Any CD ORDERBY CD DESC WHERE B is BlogEntry, '
+                                'B creation_date CD')
         blogmonths = []
         items = []
         for (blogdate,) in rset:
@@ -61,7 +60,8 @@
             self.represent(items, year, month)
         if needmore:
             url = self._cw.build_url('view', vid='blog_archive')
-            link = u'<a href="%s" title="">[%s]</a>' % (url, self._cw._('see more archives'))
+            link = u'<a href="%s" title="">[%s]</a>' % (
+                url, self._cw._('see more archives'))
             items.append( u'<li class="">%s</li>\n' % link )
         self.w(u'<div class="boxFrame">')
         if items:
@@ -72,79 +72,10 @@
         self.w(u'</div>')
 
 
-class BlogEntryArchiveBox(boxes.BoxTemplate):
-    """blog side box displaying a Blog Archive"""
-    __regid__ = 'blog_archives_box'
-    title = _('boxes_blog_archives_box')
-    order = 35
-
-    def call(self, **kwargs):
-        """display blogs archive"""
-        # XXX turn into a selector
-        count_blogentry = self._cw.execute('Any COUNT(B) WHERE B is BlogEntry')
-        if count_blogentry[0][0] > 0:
-            box = BoxWidget(self._cw._(self.title), id=self.__regid__, islist=False)
-            box.append(boxes.BoxHtml(self._cw.view('blog_archive', None, maxentries=12)))
-            box.render(self.w)
-
-
-class BlogEntryListBox(boxes.BoxTemplate):
-    """display a box with latest blogs and rss"""
-    __regid__ = 'blog_latest_box'
-    title = _('blog_latest_box')
-    visible = True # enabled by default
-    order = 34
-
-    def call(self, view=None, **kwargs):
-        # XXX turn into a selector
-        rset = self._cw.execute('Any X,T,CD ORDERBY CD DESC LIMIT 5 '
-                                'WHERE X is BlogEntry, X title T, '
-                                'X creation_date CD')
-        if not rset:
-            return
-        box = BoxWidget(self._cw._(self.title), self.__regid__, islist=True)
-        # TODO - get the date between brakets after link
-        # empty string for title argument to deactivate auto-title
-        for i in xrange(rset.rowcount):
-            entity = rset.get_entity(i, 0)
-            box.append(BoxLink(entity.absolute_url(), xml_escape(entity.dc_title())))
-        rqlst = rset.syntax_tree()
-        rqlst.set_limit(None)
-        rql = rqlst.as_string(kwargs=rset.args)
-        url = self._cw.build_url('view', rql=rql, page_size=10)
-        box.append(BoxLink(url,  u'[%s]' % self._cw._(u'see more')))
-        rss_icon = self._cw.external_resource('RSS_LOGO_16')
-        # FIXME - could use rss_url defined as a property if available
-        rss_label = u'%s <img src="%s" alt="%s"/>' % (
-            self._cw._(u'subscribe'), rss_icon, self._cw._('rss icon'))
-        rss_url = self._cw.build_url('view', vid='rss', rql=rql)
-        box.append(BoxLink(rss_url, rss_label))
-        box.render(self.w)
-
-
-class BlogEntrySummary(boxes.BoxTemplate):
-    __regid__ = 'blog_summary_box'
-    title = _('boxes_blog_summary_box')
-    order = 36
-    __select__ = boxes.BoxTemplate.__select__
-
-    def call(self, view=None, **kwargs):
-        box = BoxWidget(self._cw._(self.title), self.__regid__, islist=True)
-        rql = 'Any U, COUNT(B) GROUPBY U WHERE U is CWUser, ' \
-              'B is BlogEntry, B created_by U'
-        rset = self._cw.execute(rql)
-        for user in rset:
-            euser = self._cw.entity_from_eid(user[0])
-            box.append(BoxLink(self._cw.build_url('blogentry/%s' % euser.login),
-                                              u'%s [%s]' % (euser.name(),
-                                                            user[1])))
-        box.render(self.w)
-
-## list views ##################################################################
-
 class BlogEntrySameETypeListView(baseviews.SameETypeListView):
     __select__ = baseviews.SameETypeListView.__select__ & implements('BlogEntry')
-    countrql = 'Any COUNT(B) WHERE B is BlogEntry, B creation_date >=  %(firstday)s, B creation_date <= %(lastday)s'
+    countrql = ('Any COUNT(B) WHERE B is BlogEntry, '
+                'B creation_date >= %(firstday)s, B creation_date <= %(lastday)s')
     item_vid = 'blog'
 
     def call(self, **kwargs):
@@ -164,7 +95,6 @@
             year = year - 1
         else:
             previousmonth = month -1
-
         self.w(u'<div class="prevnext">')
         self.w(u'<span class="previousmonth">%s</span>' \
                % self.render_link(year, previousmonth,
@@ -177,27 +107,27 @@
     def render_link(self, year, month, atitle):
         firstday = datetime(year, month, 1)
         lastday = datetime(year, month, monthrange(year, month)[1])
+        args = {'firstday': firstday, 'lastday': lastday}
+        nmb_entries = self._cw.execute(self.countrql, args)[0][0]
+        if not nmb_entries:
+            return
         rql = ('Any B, BD ORDERBY BD DESC '
                'WHERE B is BlogEntry, B creation_date BD, '
-               'B creation_date >=  "%s", B creation_date <= "%s"' %
+               'B creation_date >= "%s", B creation_date <= "%s"' %
                 (firstday.strftime('%Y-%m-%d'), lastday.strftime('%Y-%m-%d')))
-        args = {'firstday':firstday, 'lastday':lastday}
-        nmb_entries = self._cw.execute(self.countrql, args)[0][0]
         label = u'%s %s [%s]' % (self._cw._(calendar.MONTHNAMES[month-1]), year,
                                  nmb_entries)
         vtitle = '%s %s' % (display_name(self._cw, 'BlogEntry', 'plural'), label)
-        url = xml_escape(self._cw.build_url('view', rql=rql, vtitle=vtitle,
-                                        month=month, year=year))
-        if self._cw.execute(rql):
-            return u'<a href="%s" title="">%s</a>' % (url, atitle)
-        return u''
+        url = self._cw.build_url('view', rql=rql, vtitle=vtitle,
+                                 month=month, year=year)
+        return u'<a href="%s">%s</a>' % (xml_escape(url), atitle)
 
 
 class BlogEntryBlogView(EntityView):
     __regid__ = 'blog'
     __select__ = implements('BlogEntry')
 
-    def cell_call(self, row, col):
+    def cell_call(self, row, col, **kwargs):
         entity = self.cw_rset.get_entity(row, col)
         w = self.w
         _ = self._cw._
@@ -231,17 +161,21 @@
         _ = lambda ertype, form='': display_name(self._cw, ertype, form)
         reldata = []
         w = reldata.append
-        if 'comments' in self._cw.vreg.schema and \
-               'BlogEntry' in self._cw.vreg.schema.rschema('comments').objects():
-            count = self._cw.execute('Any COUNT(C) WHERE C comments B, B eid %(x)s',
-                                     {'x': entity.eid}, 'x')[0][0]
+        schema = self._cw.vreg.schema
+        if 'comments' in schema and \
+               'BlogEntry' in schema.rschema('comments').objects():
+            from cubes.comment.entities import subcomments_count
+            count = subcomments_count(entity)
             if count:
                 url = xml_escape(entity.absolute_url())
-                w(u'<a href="%s">%s %s</a>' % (url, count, _('Comment', 'plural')))
+                if count > 1:
+                    label = _('Comment', 'plural')
+                else:
+                    label = _('Comment')
+                w(u'<a href="%s">%s %s</a>' % (url, count, label))
             else:
                 w(u'%s %s' % (count, _('Comment')))
-        if 'tags' in self._cw.vreg.schema and \
-               'BlogEntry' in self._cw.vreg.schema.rschema('tags').objects():
+        if 'tags' in schema and 'BlogEntry' in schema.rschema('tags').objects():
             tag_rset = entity.related('tags', 'object')
             if tag_rset:
                 w(u'%s %s' % (_('tags', 'object'), self._cw.view('csv', tag_rset)))
@@ -257,12 +191,3 @@
 
     def index_display(self, start, stop):
         return u'%s' % (int(start / self.page_size)+1)
-
-
-# WFHistoryView ###############################################################
-
-class BlogEntryWFHistoryVComponent(workflow.WFHistoryVComponent):
-    __select__ = workflow.WFHistoryVComponent.__select__ & implements('BlogEntry')
-
-    def cell_call(self, row, col, view=None):
-        pass