3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """This module contains the Linkable base class Model."""
20 '"Todd Larsen" <tlarsen@google.com>',
26 from google
.appengine
.ext
import db
28 from django
.utils
.translation
import ugettext
30 from soc
.models
import base
33 # start with ASCII lowercase
34 # (additional ASCII digit or lowercase
36 # underscore and ASCII digit or lowercase)
37 # zero or more of OR group
39 # * starting or ending underscores are *not* permitted
40 # * double internal underscores are *not* permitted
42 LINK_ID_PATTERN_CORE
= r
'[a-z](?:[0-9a-z]|_[0-9a-z])*'
43 LINK_ID_ARG_PATTERN
= r
'(?P<link_id>%s)' % LINK_ID_PATTERN_CORE
44 LINK_ID_PATTERN
= r
'^%s$' % LINK_ID_PATTERN_CORE
45 LINK_ID_REGEX
= re
.compile(LINK_ID_PATTERN
)
47 # scope path is multiple link_id chunks,
48 # each separated by a trailing /
50 SCOPE_PATH_ARG_PATTERN
= (r
'(?P<scope_path>%(link_id)s'
51 '(?:/%(link_id)s)*)' % {
52 'link_id': LINK_ID_PATTERN_CORE
})
53 SCOPE_PATH_PATTERN
= r
'^%s$' % SCOPE_PATH_ARG_PATTERN
54 SCOPE_PATH_REGEX
= re
.compile(SCOPE_PATH_PATTERN
)
56 # path is multiple link_id chunks,
57 # each separated by a trailing /
59 # followed by a single link_id with no trailing /
60 PATH_LINK_ID_ARGS_PATTERN
= (
62 '(?P<link_id>%(link_id)s)' % {
63 'scope_path' : SCOPE_PATH_ARG_PATTERN
,
64 'link_id': LINK_ID_PATTERN_CORE
})
65 PATH_LINK_ID_PATTERN
= r
'^%s$' % PATH_LINK_ID_ARGS_PATTERN
66 PATH_LINK_ID_REGEX
= re
.compile(PATH_LINK_ID_PATTERN
)
69 class Linkable(base
.ModelWithFieldAttributes
):
70 """A base class for Model classes that are "linkable".
72 Many entities in Melange are identified by a "link path" that is formed
73 by two components: a "link scope" and a "link ID".
75 The link scope is a reference to another Linkable entity, but its exact
76 usage varies depending on:
78 * the Model type of the entity
79 * the "ownership" of the entity
81 This scope represents the "context" of the entity and is *not* user-
82 editable (site Developers will be able to *carefully* edit the scope
83 of a Linkable entity, but implementing this will be tricky).
85 Appended to this "link path prefix" generated from the transitive
86 closure of the link scopes is a link ID. Unlike the rest of the link
87 path, this ID, which must be unique within the scope defined by the link
88 path, is *not* determined by context and *is* supplied by the user.
90 For example, a Document containing the FAQs for the Apache Software
91 Foundation participation in GSoC 2009 program sponsored
92 by Google could be given a link ID by the Apache organization
93 administrator of "faqs", but the rest of the link path would be
94 determined by the transitive closure of the scopes of the Document:
96 google/gsoc2009/asf/faqs
98 | | | +--------- link ID assigned by Apache admin
100 | | +------------- Apache org link ID (immutable)
102 | +-------------------- GSoC 2009 program link ID (immutable)
104 +---------------------------- Google sponsor link ID (immutable)
106 For many entities, link IDs, once specified, are immutable, since
107 changing them can break bookmarked URLs. Changing the link IDs of
108 "leaf" entities (such as the Document in the example above) could
111 #: Required field storing "ID" used in URL links. ASCII characters,
112 #: digits and underscores only. Valid link IDs successfully match
113 #: the LINK_ID_REGEX.
114 link_id
= db
.StringProperty(required
=True,
115 verbose_name
=ugettext('Link ID'))
116 link_id
.help_text
= ugettext(
117 'Link ID is used as part of various URL links throughout the site.'
118 ' <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> '
119 ' characters, digits, and underscores only.'
120 ' The regexp used to validate is "%s".') % LINK_ID_PATTERN_CORE
122 #: Optional Self Reference property to another Linkable entity which defines
123 #: the "scope" of this Linkable entity. The back-reference in the Linkable
124 #: model is a Query named 'links'.
125 scope
= db
.SelfReferenceProperty(required
=False,
126 collection_name
='links', verbose_name
=ugettext('Link Scope'))
127 scope
.help_text
= ugettext(
128 'Reference to another Linkable entity that defines the "scope" of'
129 ' this Linkable entity.')
131 #: Hidden (not displayed to users or editable in forms) cache of the string
132 #: representation of the transitive closure of scopes, for use in URLs.
133 #: The multiple queries required to produce this string for entities in
134 #: deeply-nested scopes can be prohibitively expensive. The scope of an
135 #: entity is not expected to change frequently (only for move, copy, and
136 #: maybe re-parenting operations), so this property is not likely to need
138 scope_path
= db
.StringProperty(required
=False,
139 verbose_name
=ugettext('Scope path'))
140 scope_path
.help_text
= ugettext(
141 'Cache of the string form of the entity scope.')