Code

Ticket #11727: contrib.breadcrumbs.diff

File contrib.breadcrumbs.diff, 10.5 KB (added by chronos, 5 years ago)

The breadcrumbs system.

Line 
1diff -urN django-original/contrib/breadcrumbs/breadcrumbs.py django/contrib/breadcrumbs/breadcrumbs.py
2--- django-original/contrib/breadcrumbs/breadcrumbs.py  1969-12-31 21:00:00.000000000 -0300
3+++ django/contrib/breadcrumbs/breadcrumbs.py   2009-08-13 16:48:45.000000000 -0300
4@@ -0,0 +1,167 @@
5+"""
6+Classes to add request.breadcrumbs as one class to have a list of breadcrumbs
7+TODO: maybe is better to move to contrib/breadcrumbs
8+"""
9+
10+from django.conf import settings
11+from django.utils.translation import ugettext_lazy as _
12+from django.utils.safestring import mark_safe
13+from django.utils.text import force_unicode
14+import sys
15+
16+class Singleton(object):
17+    """
18+    We use a simple singleton pattern in Breadcrumbs.
19+    Example from http://svn.ademar.org/code/trunk/junk-code/singleton_vs_borg.py
20+    """
21+    def __new__(cls, *args, **kwds):
22+        it = cls.__dict__.get("__it__")
23+        if it is not None:
24+            return it
25+        cls.__it__ = it = object.__new__(cls)
26+        it._1st_init(*args, **kwds)
27+        return it
28+
29+    def _1st_init(self, *args, **kwds):
30+        pass
31+
32+class BreadcrumbsInvalidFormat(Exception):
33+    """
34+    Simple exception that can be extended
35+    """
36+    pass
37+
38+class BreadcrumbsNotSet(Exception):
39+    """
40+    Raised in utils.breadcrumbs_for_flatpages when we not have breadcrumbs in
41+    request.
42+    """
43+    pass
44+
45+class Breadcrumb(object):
46+    """
47+    Breadcrumb can have methods to customize breadcrumb object, Breadcrumbs
48+    class send to us name and url.
49+    """
50+    def __init__(self,name,url):
51+        # HERE
52+        #
53+        # If I don't use force_unicode, always runs ok, but have problems on
54+        # template with unicode text
55+        self.name = name
56+        self.url = url
57+
58+    def __str__(self):
59+        return self.__unicode__()
60+
61+    def __unicode__(self):
62+        return u"%s,%s" % (self.name,self.url)
63+
64+    def __repr__(self):
65+        return u"Breadcrumb <%s,%s>" % (self.name,self.url)
66+
67+class Breadcrumbs(Singleton):
68+    """
69+    Breadcrumbs maintain a list of breadcrumbs that you can get interating with
70+    class or with get_breadcrumbs().
71+    """
72+    def _1st_init(self,*args,**kwargs):
73+        """
74+        singleton function that start some variables
75+        """
76+        self._clean()
77+        self.__init__(*args,**kwargs)
78+
79+    def __call__(self,*args,**kwargs):
80+        if not len(args) and not len(kwargs):
81+            return self
82+        return self.__init__(*args,**kwargs)
83+
84+    def _clean(self):
85+        self.__bds = []
86+        self.__autohome=settings.BREADCRUMBS_AUTO_HOME
87+        self.__urls =[]
88+
89+    def __init__(self,*args,**kwargs):
90+        """
91+        Call validate and if ok, call fill bd
92+        """
93+        if settings.BREADCRUMBS:
94+
95+            # fill home if settings.BREADCRUMBS_AUTO_HOME is True
96+            if self.__autohome and len(self.__bds) == 0:
97+                self.__fill_bds( ( _("Home"), u"/" ) )
98+
99+            # match Breadcrumbs( 'name', 'url' )
100+            if len(args) == 2 and type(args[0]) not in (list,tuple):
101+                if(self.__validate(args,0)):
102+                    self.__fill_bds( args )
103+            # match ( ( 'name', 'url'), ..) and samething with list
104+            elif len(args) == 1 and type(args[0]) in (list,tuple) \
105+                    and len(args[0]) > 0:
106+                for i,arg in enumerate(args[0]):
107+                    if self.__validate(arg,i):
108+                        self.__fill_bds( arg )
109+            # try to ( obj1, obj2, ... ) and samething with list
110+            else:
111+                for i,arg in enumerate(args):
112+                    if(self.__validate(arg,i)):
113+                        self.__fill_bds( arg )
114+
115+    def __validate(self,obj,index):
116+        """
117+        check for object type and return a string as name for each item of a
118+        list or tuple with items, if error was found raise
119+        BreadcrumbsInvalidFormat
120+        """
121+        # for list or tuple
122+        if type(obj) in (list,tuple):
123+            if len(obj) == 2:
124+                if (not obj[0] and not obj[1]) or \
125+                ( type(obj[0]) not in (str,unicode) and \
126+                  type(obj[1]) not in (str,unicode)):
127+                    raise BreadcrumbsInvalidFormat(u"Invalid format for \
128+                        breadcrumb %s in %s" % (index,type(obj).__name__))
129+            if len(obj) != 2:
130+                raise BreadcrumbsInvalidFormat(
131+                    u"Wrong itens number in breadcrumb %s in %s. \
132+                    You need to send as example (name,url)" % \
133+                    (index,type(obj).__name__)
134+                )
135+        # for objects
136+        elif not hasattr(obj,'name') and not hasattr(obj,'url'):
137+            raise BreadcrumbsInvalidFormat(u"You need to use a tuple like"+ \
138+                " (name,url) or object with name and url attributes for" + \
139+                "breadcrumb.")
140+        return True
141+
142+    def __fill_bds(self,bd):
143+        """
144+        simple interface to add Breadcrumb to bds
145+        """
146+        if hasattr(bd,'name') and hasattr(bd,'url'):
147+            bd = Breadcrumb(bd.name,bd.url)
148+        else:
149+            bd = Breadcrumb(*bd)
150+        if bd.url not in self.__urls:
151+            self.__bds.append(bd)
152+            self.__urls.append(bd.url)
153+
154+    def __iter__(self):
155+        return iter(self.__bds)
156+
157+    def __getitem__(self,key):
158+        return self.__bds[key]
159+
160+    def __repr__(self):
161+        return self.__unicode__()
162+
163+    def __str__(self):
164+        return self.__unicode__()
165+
166+    def __unicode__(self):
167+        return u"Breadcrumbs <%s>" % u", ".join([mark_safe(item.name) for item in \
168+            self[:10]] + [u' ...'])
169+
170+    def all(self):
171+        return self.__bds
172diff -urN django-original/contrib/breadcrumbs/HowToUse.txt django/contrib/breadcrumbs/HowToUse.txt
173--- django-original/contrib/breadcrumbs/HowToUse.txt    1969-12-31 21:00:00.000000000 -0300
174+++ django/contrib/breadcrumbs/HowToUse.txt     2009-08-13 16:48:45.000000000 -0300
175@@ -0,0 +1,31 @@
176+To use breadcrumbs, after unpack this package you need to follow some steps:
177+
178+1 - Set in your settings.py:
179+
180+       #Options to enable Breadcrumbs package:
181+       BREADCRUMBS = False # Enable breadcrumbs
182+       BREADCRUMBS_AUTO_HOME = False # If True, all times first link is (_("Home"),u"/")
183+
184+       # Add BreadcrumbsMiddleware in your MIDDLEWARE_CLASSES, Ex:
185+
186+       MIDDLEWARE_CLASSES = (
187+       ...
188+       'django.contrib.breadcrumbs.middleware.BreadcrumbsMiddleware',
189+       )
190+
191+2 - For each view that you want to show on Breadcrumbs, you can use some of
192+examples bellow:
193+
194+       # For two itens (or more) on path
195+       request.breadcrumbs(
196+               (_("Profile"),reverse(UserShowProfile)),
197+               (_("Favorites"),reverse(UserFavs))
198+       )
199+
200+       # For one item on path
201+       request.breadcrumbs( _("Profile"),reverse(UserShowProfile) )
202+
203+       # Without reverse:
204+       request.breadcrumbs( u"Name", "URL" )
205+
206+Breadcrumbs have a path to support flatpages without problems :)
207diff -urN django-original/contrib/breadcrumbs/__init__.py django/contrib/breadcrumbs/__init__.py
208--- django-original/contrib/breadcrumbs/__init__.py     1969-12-31 21:00:00.000000000 -0300
209+++ django/contrib/breadcrumbs/__init__.py      2009-08-13 16:48:45.000000000 -0300
210@@ -0,0 +1 @@
211+from breadcrumbs import Breadcrumb
212diff -urN django-original/contrib/breadcrumbs/middleware.py django/contrib/breadcrumbs/middleware.py
213--- django-original/contrib/breadcrumbs/middleware.py   1969-12-31 21:00:00.000000000 -0300
214+++ django/contrib/breadcrumbs/middleware.py    2009-08-13 16:48:45.000000000 -0300
215@@ -0,0 +1,7 @@
216+from breadcrumbs import Breadcrumbs
217+
218+class BreadcrumbsMiddleware(object):
219+
220+    def process_request(self,request):
221+        request.breadcrumbs = Breadcrumbs()
222+        request.breadcrumbs._clean()
223diff -urN django-original/contrib/breadcrumbs/tests.py django/contrib/breadcrumbs/tests.py
224--- django-original/contrib/breadcrumbs/tests.py        1969-12-31 21:00:00.000000000 -0300
225+++ django/contrib/breadcrumbs/tests.py 2009-08-13 16:48:45.000000000 -0300
226@@ -0,0 +1,21 @@
227+# run this script on your shell
228+from django.contrib.breadcrumbs import Breadcrumbs
229+
230+bds = Breadcrumbs()
231+
232+# fill one per time
233+for i in range(5):
234+    bds( 'name%s' % i,'url%s' % i  )
235+
236+# create a simple class to emulate one object with name and url
237+class emulatedobj(object):
238+    def __init__(self,*args):
239+        self.name = args[0]
240+        self.url = args[1]
241+
242+# add 10 objects
243+bds( [ emulatedobj('name %s' % (i+10), 'url %s' % (i+10)) for i in range(10) ] )
244+
245+# print all
246+for bd in bds:
247+    print bd.name,bd.url
248diff -urN django-original/contrib/breadcrumbs/utils.py django/contrib/breadcrumbs/utils.py
249--- django-original/contrib/breadcrumbs/utils.py        1969-12-31 21:00:00.000000000 -0300
250+++ django/contrib/breadcrumbs/utils.py 2009-08-13 16:48:45.000000000 -0300
251@@ -0,0 +1,49 @@
252+from breadcrumbs import Breadcrumbs,BreadcrumbsNotSet
253+from django.contrib.flatpages.models import FlatPage
254+from django.http import Http404
255+
256+def breadcrumbs_for_flatpages(request,flatpage):
257+
258+    if not hasattr(request,'breadcrumbs') or \
259+        not isinstance(request.breadcrumbs,Breadcrumbs):
260+        raise BreadcrumbNotSet(u"You need to setup breadcrumbs to use this " + \
261+                "function.")
262+
263+    if not isinstance(flatpage,FlatPage) or \
264+        not hasattr(flatpage,'id'):
265+        raise TypeError(u"flatpage argument isn't a FlatPage instance or " + \
266+            "not have id.")
267+
268+    paths = []
269+    for part in request.path_info.split(u"/"):
270+        # When split we have u"" for slashes
271+        if len(part) == 0:
272+            continue
273+        # Add slash again
274+        if not part.startswith(u"/"):
275+            part = u"/"+part
276+        if not part.endswith(u"/"):
277+            part = part+u"/"
278+        # If we have something on paths, url for flatpage is composed of what we
279+        # have in path + part. Note that strins in path not have last slash, but
280+        # part have.
281+        if len(paths) > 0:
282+            url = u"".join(paths+[part])
283+        else:
284+            url = part
285+        # if is same url we don't hit database again
286+        # else, get page from FlatPage. If page doesn't exist, we allow raise
287+        # 404 because it is a url design problem, not flatpages or breadcrumbs
288+        # problem.
289+        if url == flatpage.url:
290+            request.breadcrumbs(flatpage.title,flatpage.url)
291+        else:
292+            try:
293+                f = FlatPage.objects.get(url=url)
294+            except FlatPage.DoesNotExist:
295+                raise Http404
296+            else:
297+                request.breadcrumbs(f.title,f.url)
298+        # add last part of path in paths with one slash
299+        paths.append(u"/"+url[1:-1].rpartition(u"/")[-1])
300+