| 1 |
from django.db import models |
|---|
| 2 |
from django.db.models import signals |
|---|
| 3 |
from django.core.cache import cache |
|---|
| 4 |
from django.contrib import admin |
|---|
| 5 |
from django.contrib.auth.models import User |
|---|
| 6 |
from django.contrib.contenttypes.models import ContentType |
|---|
| 7 |
from django.contrib.contenttypes import generic |
|---|
| 8 |
from sphene.community.sphpermalink import sphpermalink |
|---|
| 9 |
from django.utils.translation import ugettext as _, ugettext_lazy |
|---|
| 10 |
from django.db import connection |
|---|
| 11 |
import logging |
|---|
| 12 |
import re |
|---|
| 13 |
logger = logging.getLogger('sphene.community.models') |
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
class Group(models.Model): |
|---|
| 17 |
name = models.CharField(max_length = 250) |
|---|
| 18 |
longname = models.CharField(max_length = 250) |
|---|
| 19 |
default_theme = models.ForeignKey('Theme', null = True, blank = True) |
|---|
| 20 |
parent = models.ForeignKey('Group', null = True, blank = True) |
|---|
| 21 |
baseurl = models.CharField(max_length = 250, help_text = 'The base URL under which this group will be available. Example: sct.sphene.net') |
|---|
| 22 |
|
|---|
| 23 |
def get_name(self): |
|---|
| 24 |
return self.longname or self.name |
|---|
| 25 |
|
|---|
| 26 |
def recursiveName(self): |
|---|
| 27 |
recname = '' |
|---|
| 28 |
if self.parent: |
|---|
| 29 |
recname = self.parent.recursiveName() + ' / ' |
|---|
| 30 |
return recname + self.name |
|---|
| 31 |
|
|---|
| 32 |
def get_member(self, user): |
|---|
| 33 |
try: |
|---|
| 34 |
return GroupMember.objects.get( group = self, |
|---|
| 35 |
user = user, ) |
|---|
| 36 |
except GroupMember.DoesNotExist: |
|---|
| 37 |
return None |
|---|
| 38 |
|
|---|
| 39 |
def get_baseurl(self): |
|---|
| 40 |
""" |
|---|
| 41 |
Returns the "base URL" including http:// and without tailing slash. |
|---|
| 42 |
""" |
|---|
| 43 |
url = self.baseurl |
|---|
| 44 |
if not (url.startswith('http://') or url.startswith('https://')): |
|---|
| 45 |
url = 'http://%s' % url |
|---|
| 46 |
if url.endswith('/'): |
|---|
| 47 |
url = url[0:-1] |
|---|
| 48 |
return url |
|---|
| 49 |
|
|---|
| 50 |
def __unicode__(self): |
|---|
| 51 |
return self.name; |
|---|
| 52 |
|
|---|
| 53 |
def get_group(name): |
|---|
| 54 |
"""Return a `Group` by name. |
|---|
| 55 |
|
|---|
| 56 |
Cache the group and return the cached version if available. |
|---|
| 57 |
Raise `Group.DoesNotExist` if not found. |
|---|
| 58 |
""" |
|---|
| 59 |
group = cache.get(_get_group_cache_key(name)) |
|---|
| 60 |
if group is not None: |
|---|
| 61 |
return group |
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
group = Group.objects.get( name__exact = name ) |
|---|
| 65 |
cache.add(_get_group_cache_key(name), group) |
|---|
| 66 |
|
|---|
| 67 |
return group |
|---|
| 68 |
|
|---|
| 69 |
def invalidate_group_cache(sender, instance, **kwargs): |
|---|
| 70 |
"""Signal handler to remove a `Group` from the cache.""" |
|---|
| 71 |
if instance.name: |
|---|
| 72 |
cache.delete(_get_group_cache_key(instance.name)) |
|---|
| 73 |
|
|---|
| 74 |
signals.pre_save.connect( |
|---|
| 75 |
invalidate_group_cache, |
|---|
| 76 |
sender=Group) |
|---|
| 77 |
|
|---|
| 78 |
def _get_group_cache_key(name): |
|---|
| 79 |
return "__group__" + name |
|---|
| 80 |
|
|---|
| 81 |
USERLEVEL_CHOICES = ( |
|---|
| 82 |
(0, ugettext_lazy('Normal User')), |
|---|
| 83 |
(5, ugettext_lazy('Administrator')), |
|---|
| 84 |
) |
|---|
| 85 |
|
|---|
| 86 |
class GroupMember(models.Model): |
|---|
| 87 |
group = models.ForeignKey( Group ) |
|---|
| 88 |
user = models.ForeignKey( User, ) |
|---|
| 89 |
userlevel = models.IntegerField( choices = USERLEVEL_CHOICES ) |
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
changelog = ( ( '2008-04-06 00', 'alter', 'ADD userlevel integer', ), |
|---|
| 93 |
( '2008-04-06 01', 'update', 'SET userlevel = 0', ), |
|---|
| 94 |
( '2008-04-06 02', 'alter', 'ALTER userlevel SET NOT NULL', ), ) |
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
def get_userlevel_str(self): |
|---|
| 98 |
for value, str in USERLEVEL_CHOICES: |
|---|
| 99 |
if value == self.userlevel: |
|---|
| 100 |
return str; |
|---|
| 101 |
|
|---|
| 102 |
|
|---|
| 103 |
|
|---|
| 104 |
class Theme(models.Model): |
|---|
| 105 |
name = models.CharField(max_length = 250) |
|---|
| 106 |
path = models.CharField(max_length = 250) |
|---|
| 107 |
|
|---|
| 108 |
def __unicode__(self): |
|---|
| 109 |
return self.name; |
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
NAVIGATION_URL_TYPES = ( |
|---|
| 113 |
(0, 'Relative (e.g. /wiki/show/Start)'), |
|---|
| 114 |
(1, 'Absolute (e.g. http://sphene.net') |
|---|
| 115 |
) |
|---|
| 116 |
|
|---|
| 117 |
NAVIGATION_TYPES = ( |
|---|
| 118 |
(0, 'Left Main Navigation'), |
|---|
| 119 |
(1, 'Top navigation') |
|---|
| 120 |
) |
|---|
| 121 |
|
|---|
| 122 |
class Navigation(models.Model): |
|---|
| 123 |
group = models.ForeignKey(Group) |
|---|
| 124 |
label = models.CharField(max_length = 250) |
|---|
| 125 |
href = models.CharField(max_length = 250) |
|---|
| 126 |
urltype = models.IntegerField( default = 0, choices = NAVIGATION_URL_TYPES ) |
|---|
| 127 |
sortorder = models.IntegerField( default = 100 ) |
|---|
| 128 |
navigationType = models.IntegerField( default = 0, choices = NAVIGATION_TYPES ) |
|---|
| 129 |
|
|---|
| 130 |
|
|---|
| 131 |
def __unicode__(self): |
|---|
| 132 |
return self.label |
|---|
| 133 |
|
|---|
| 134 |
class Meta: |
|---|
| 135 |
ordering = ['sortorder'] |
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
class ApplicationChangelog(models.Model): |
|---|
| 139 |
app_label = models.CharField(max_length = 250) |
|---|
| 140 |
model = models.CharField(max_length = 250) |
|---|
| 141 |
version = models.CharField(max_length = 250) |
|---|
| 142 |
applied = models.DateTimeField() |
|---|
| 143 |
|
|---|
| 144 |
class Meta: |
|---|
| 145 |
get_latest_by = 'applied' |
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
from sphene.community.sphsettings import get_sph_setting |
|---|
| 149 |
|
|---|
| 150 |
|
|---|
| 151 |
class CommunityUserProfile(models.Model): |
|---|
| 152 |
user = models.ForeignKey( User, unique = True) |
|---|
| 153 |
displayname = models.CharField(max_length = 250) |
|---|
| 154 |
public_emailaddress = models.CharField(max_length = 250) |
|---|
| 155 |
|
|---|
| 156 |
avatar = models.ImageField( height_field = 'avatar_height', |
|---|
| 157 |
width_field = 'avatar_width', |
|---|
| 158 |
upload_to = get_sph_setting('community_avatar_upload_to'), |
|---|
| 159 |
blank = True, null = True, ) |
|---|
| 160 |
avatar_height = models.IntegerField(blank = True, null = True, ) |
|---|
| 161 |
avatar_width = models.IntegerField(blank = True, null = True, ) |
|---|
| 162 |
|
|---|
| 163 |
|
|---|
| 164 |
changelog = ( ( '2007-08-10 00', 'alter', 'ADD avatar varchar(100)' ), |
|---|
| 165 |
( '2007-08-10 01', 'alter', 'ADD avatar_height integer' ), |
|---|
| 166 |
( '2007-08-10 02', 'alter', 'ADD avatar_width integer' ), |
|---|
| 167 |
( '2008-04-10 00', 'alter', 'ADD displayname varchar(250)' ), |
|---|
| 168 |
( '2008-04-10 01', 'update', "SET displayname = ''" ), |
|---|
| 169 |
( '2008-04-10 02', 'alter', 'ALTER displayname SET NOT NULL' ), |
|---|
| 170 |
) |
|---|
| 171 |
|
|---|
| 172 |
class CommunityUserProfileField(models.Model): |
|---|
| 173 |
""" User profile fields, configurable through the django admin |
|---|
| 174 |
interface. """ |
|---|
| 175 |
name = models.CharField(max_length = 250) |
|---|
| 176 |
help_text = models.CharField(max_length = 250, blank = True, help_text = 'An optional help text displayed to the user.' ) |
|---|
| 177 |
regex = models.CharField(max_length = 250, blank = True, help_text = 'An optional regular expression to validate user input.', ) |
|---|
| 178 |
renderstring = models.CharField(max_length = 250, blank = True, help_text = 'An optional render string how the value should be displayed in the profile (e.g. <a href="%(value)s">%(value)s</a> - default: %(value)s' ) |
|---|
| 179 |
sortorder = models.IntegerField() |
|---|
| 180 |
|
|---|
| 181 |
class Meta: |
|---|
| 182 |
ordering = [ 'sortorder' ] |
|---|
| 183 |
|
|---|
| 184 |
|
|---|
| 185 |
class CommunityUserProfileFieldValue(models.Model): |
|---|
| 186 |
user_profile = models.ForeignKey( CommunityUserProfile ) |
|---|
| 187 |
profile_field = models.ForeignKey( CommunityUserProfileField ) |
|---|
| 188 |
|
|---|
| 189 |
value = models.CharField( max_length = 250 ) |
|---|
| 190 |
|
|---|
| 191 |
class Meta: |
|---|
| 192 |
unique_together = (("user_profile", "profile_field"),) |
|---|
| 193 |
|
|---|
| 194 |
class GroupTemplate(models.Model): |
|---|
| 195 |
""" |
|---|
| 196 |
Represents a group specific template which can be used to overload |
|---|
| 197 |
any django template from the filesystem. |
|---|
| 198 |
""" |
|---|
| 199 |
group = models.ForeignKey(Group) |
|---|
| 200 |
template_name = models.CharField( max_length = 250, db_index = True ) |
|---|
| 201 |
source = models.TextField() |
|---|
| 202 |
|
|---|
| 203 |
def __unicode__(self): |
|---|
| 204 |
return self.template_name |
|---|
| 205 |
|
|---|
| 206 |
class Meta: |
|---|
| 207 |
unique_together = (("group", "template_name"),) |
|---|
| 208 |
|
|---|
| 209 |
|
|---|
| 210 |
|
|---|
| 211 |
class PermissionFlag(models.Model): |
|---|
| 212 |
""" |
|---|
| 213 |
Permission flags are predefined (in the code) flags of user rights. |
|---|
| 214 |
Very similar to django's permissions. |
|---|
| 215 |
|
|---|
| 216 |
(I decided against using django's permissions for the sake of simplicity.. |
|---|
| 217 |
i don't like the idea of auto generating permissions which aren't used |
|---|
| 218 |
in the application code (but only within the django administration)) |
|---|
| 219 |
""" |
|---|
| 220 |
name = models.CharField( max_length = 250, unique = True ) |
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 |
sph_permission_flags = { 'group_administrator': |
|---|
| 224 |
'Users with this permission flag have all permissions.', |
|---|
| 225 |
|
|---|
| 226 |
'community_manage_roles': |
|---|
| 227 |
'User have permission to create, edit and assign roles.', |
|---|
| 228 |
} |
|---|
| 229 |
|
|---|
| 230 |
|
|---|
| 231 |
def __unicode__(self): |
|---|
| 232 |
return self.name |
|---|
| 233 |
|
|---|
| 234 |
class Role(models.Model): |
|---|
| 235 |
""" |
|---|
| 236 |
A role is a user defined collection of so called permission flags. |
|---|
| 237 |
""" |
|---|
| 238 |
name = models.CharField( max_length = 250 ) |
|---|
| 239 |
group = models.ForeignKey( Group ) |
|---|
| 240 |
|
|---|
| 241 |
permission_flags = models.ManyToManyField( PermissionFlag, related_name = 'roles' ) |
|---|
| 242 |
|
|---|
| 243 |
|
|---|
| 244 |
def get_permission_flag_string(self): |
|---|
| 245 |
return ", ".join( [flag.name for flag in self.permission_flags.all()] ) |
|---|
| 246 |
|
|---|
| 247 |
def __unicode__(self): |
|---|
| 248 |
return '%s - %s' % (self.group.name, self.name) |
|---|
| 249 |
|
|---|
| 250 |
def get_absolute_editurl(self): |
|---|
| 251 |
return ('sphene.community.views.admin_permission_role_edit', (), { 'groupName': self.group.name, 'role_id': self.id, } ) |
|---|
| 252 |
get_absolute_editurl = sphpermalink(get_absolute_editurl) |
|---|
| 253 |
|
|---|
| 254 |
def get_absolute_memberlisturl(self): |
|---|
| 255 |
return ('sphene.community.views.admin_permission_role_member_list', (), { 'groupName': self.group.name, 'role_id': self.id, } ) |
|---|
| 256 |
get_absolute_memberlisturl = sphpermalink(get_absolute_memberlisturl) |
|---|
| 257 |
|
|---|
| 258 |
def get_absolute_memberaddurl(self): |
|---|
| 259 |
return ('sphene.community.views.admin_permission_role_member_add', (), { 'groupName': self.group.name, 'role_id': self.id, } ) |
|---|
| 260 |
get_absolute_memberaddurl = sphpermalink(get_absolute_memberaddurl) |
|---|
| 261 |
|
|---|
| 262 |
def get_absolute_groupmemberaddurl(self): |
|---|
| 263 |
return ('sphene.community.views.admin_permission_role_groupmember_add', (), { 'groupName': self.group.name, 'role_id': self.id, } ) |
|---|
| 264 |
get_absolute_groupmemberaddurl = sphpermalink(get_absolute_groupmemberaddurl) |
|---|
| 265 |
|
|---|
| 266 |
class Meta: |
|---|
| 267 |
unique_together = (('name', 'group'),) |
|---|
| 268 |
|
|---|
| 269 |
|
|---|
| 270 |
|
|---|
| 271 |
class RoleMember(models.Model): |
|---|
| 272 |
""" |
|---|
| 273 |
A role member is the relation between a given role and a |
|---|
| 274 |
1.) user OR 2.) rolegroup - one of those two have to be null ! |
|---|
| 275 |
|
|---|
| 276 |
This relation can have additional limitations - e.g. for the board |
|---|
| 277 |
it might only be active within one given category - |
|---|
| 278 |
see RoleMemberLimitation. |
|---|
| 279 |
|
|---|
| 280 |
If there are no limitations (has_limitations = False) then the role |
|---|
| 281 |
is active for the user globally within the role's group. |
|---|
| 282 |
""" |
|---|
| 283 |
role = models.ForeignKey( Role ) |
|---|
| 284 |
user = models.ForeignKey( User, null = True ) |
|---|
| 285 |
rolegroup = models.ForeignKey( 'RoleGroup', null = True ) |
|---|
| 286 |
|
|---|
| 287 |
has_limitations = models.BooleanField() |
|---|
| 288 |
|
|---|
| 289 |
|
|---|
| 290 |
changelog = ( ( '2008-04-15 00', 'alter', 'ALTER user_id DROP NOT NULL', ), |
|---|
| 291 |
( '2008-04-15 01', 'alter', 'ADD rolegroup_id integer REFERENCES community_rolegroup(id)', ), ) |
|---|
| 292 |
|
|---|
| 293 |
def get_limitations_string(self): |
|---|
| 294 |
if not self.has_limitations: |
|---|
| 295 |
return "None" |
|---|
| 296 |
limitation = self.rolememberlimitation_set.get() |
|---|
| 297 |
return "%s: %s" % (limitation.object_type.model_class()._meta.object_name, unicode(limitation.content_object)) |
|---|
| 298 |
|
|---|
| 299 |
|
|---|
| 300 |
|
|---|
| 301 |
class RoleMemberLimitation(models.Model): |
|---|
| 302 |
""" |
|---|
| 303 |
Limits the membership of a user to a role by only applying to a |
|---|
| 304 |
specific object. |
|---|
| 305 |
""" |
|---|
| 306 |
role_member = models.ForeignKey( RoleMember ) |
|---|
| 307 |
|
|---|
| 308 |
object_type = models.ForeignKey(ContentType) |
|---|
| 309 |
object_id = models.PositiveIntegerField(db_index = True) |
|---|
| 310 |
|
|---|
| 311 |
content_object = generic.GenericForeignKey(ct_field = 'object_type') |
|---|
| 312 |
|
|---|
| 313 |
class Meta: |
|---|
| 314 |
unique_together = (('role_member', 'object_type', 'object_id'),) |
|---|
| 315 |
|
|---|
| 316 |
|
|---|
| 317 |
|
|---|
| 318 |
|
|---|
| 319 |
class RoleGroup(models.Model): |
|---|
| 320 |
""" |
|---|
| 321 |
a role group can be used to add common restrictions for a given group |
|---|
| 322 |
of users. |
|---|
| 323 |
""" |
|---|
| 324 |
group = models.ForeignKey(Group) |
|---|
| 325 |
name = models.CharField(max_length = 250) |
|---|
| 326 |
|
|---|
| 327 |
def __unicode__(self): |
|---|
| 328 |
return self.name |
|---|
| 329 |
|
|---|
| 330 |
def get_absolute_editurl(self): |
|---|
| 331 |
return ('sphene.community.views.admin_permission_rolegroup_edit', (), { 'groupName': self.group.name, 'rolegroup_id': self.id, } ) |
|---|
| 332 |
get_absolute_editurl = sphpermalink(get_absolute_editurl) |
|---|
| 333 |
|
|---|
| 334 |
class Meta: |
|---|
| 335 |
unique_together = ('group', 'name',) |
|---|
| 336 |
|
|---|
| 337 |
|
|---|
| 338 |
class RoleGroupMember(models.Model): |
|---|
| 339 |
rolegroup = models.ForeignKey(RoleGroup) |
|---|
| 340 |
user = models.ForeignKey(User) |
|---|
| 341 |
|
|---|
| 342 |
class Meta: |
|---|
| 343 |
unique_together = ('rolegroup', 'user',) |
|---|
| 344 |
|
|---|
| 345 |
|
|---|
| 346 |
|
|---|
| 347 |
|
|---|
| 348 |
|
|---|
| 349 |
|
|---|
| 350 |
|
|---|
| 351 |
|
|---|
| 352 |
|
|---|
| 353 |
|
|---|
| 354 |
|
|---|
| 355 |
|
|---|
| 356 |
tag_sanitize_regex = re.compile(r'[^\w]+', re.S | re.U) |
|---|
| 357 |
|
|---|
| 358 |
def tag_sanitize(tag_label): |
|---|
| 359 |
return tag_sanitize_regex.sub('', tag_label).lower() |
|---|
| 360 |
|
|---|
| 361 |
def tag_set_labels(model_instance, tag_labels): |
|---|
| 362 |
""" |
|---|
| 363 |
sets the tags of the given model_instance (which must exists already!) |
|---|
| 364 |
to the given tag labels (which must be TagLabel models.) |
|---|
| 365 |
- removes all existing tags. |
|---|
| 366 |
|
|---|
| 367 |
returns True if anything has changed, False otherwise. |
|---|
| 368 |
""" |
|---|
| 369 |
|
|---|
| 370 |
model_type = ContentType.objects.get_for_model(model_instance) |
|---|
| 371 |
|
|---|
| 372 |
|
|---|
| 373 |
tag_labels = list(tag_labels) |
|---|
| 374 |
old_tag_labels = TaggedItem.objects.filter( content_type__pk = model_type.id, |
|---|
| 375 |
object_id = model_instance.id, ) |
|---|
| 376 |
|
|---|
| 377 |
if len(tag_labels) == old_tag_labels.count(): |
|---|
| 378 |
if len(tag_labels) == 0: |
|---|
| 379 |
|
|---|
| 380 |
return False |
|---|
| 381 |
|
|---|
| 382 |
for tagged_item in old_tag_labels: |
|---|
| 383 |
found = False |
|---|
| 384 |
for tag_label in tag_labels: |
|---|
| 385 |
if tag_label == tagged_item.tag_label: |
|---|
| 386 |
found = True |
|---|
| 387 |
tag_labels.remove(tag_label) |
|---|
| 388 |
break |
|---|
| 389 |
|
|---|
| 390 |
if not found: |
|---|
| 391 |
break |
|---|
| 392 |
|
|---|
| 393 |
if len(tag_labels) == 0: |
|---|
| 394 |
|
|---|
| 395 |
|
|---|
| 396 |
return False |
|---|
| 397 |
|
|---|
| 398 |
|
|---|
| 399 |
|
|---|
| 400 |
TaggedItem.objects.filter( content_type__pk = model_type.id, |
|---|
| 401 |
object_id = model_instance.id, ).delete() |
|---|
| 402 |
|
|---|
| 403 |
|
|---|
| 404 |
|
|---|
| 405 |
for tag_label in tag_labels: |
|---|
| 406 |
t = TaggedItem( object = model_instance, |
|---|
| 407 |
tag_label = tag_label ) |
|---|
| 408 |
t.save() |
|---|
| 409 |
|
|---|
| 410 |
|
|---|
| 411 |
return True |
|---|
| 412 |
|
|---|
| 413 |
|
|---|
| 414 |
def tag_get_labels(model_instance): |
|---|
| 415 |
""" |
|---|
| 416 |
returns all TagLabel objects for the given model_instance. |
|---|
| 417 |
""" |
|---|
| 418 |
model_type = ContentType.objects.get_for_model(model_instance) |
|---|
| 419 |
tagged_items = TaggedItem.objects.filter( content_type__pk = model_type.id, |
|---|
| 420 |
object_id = model_instance.id, ) |
|---|
| 421 |
|
|---|
| 422 |
tag_labels = list() |
|---|
| 423 |
for tagged_item in tagged_items: |
|---|
| 424 |
tag_labels.append( tagged_item.tag_label ) |
|---|
| 425 |
|
|---|
| 426 |
return tag_labels |
|---|
| 427 |
|
|---|
| 428 |
def tag_get_or_create_label(group, tag_label_str): |
|---|
| 429 |
if tag_label_str == '': |
|---|
| 430 |
|
|---|
| 431 |
return None |
|---|
| 432 |
|
|---|
| 433 |
|
|---|
| 434 |
try: |
|---|
| 435 |
tag_label = TagLabel.objects.get( tag__group = group, |
|---|
| 436 |
label__exact = tag_label_str ) |
|---|
| 437 |
except TagLabel.DoesNotExist: |
|---|
| 438 |
|
|---|
| 439 |
tag_name_str = tag_sanitize(tag_label_str) |
|---|
| 440 |
|
|---|
| 441 |
try: |
|---|
| 442 |
tag = Tag.objects.get( group = group, |
|---|
| 443 |
name__exact = tag_name_str ) |
|---|
| 444 |
except Tag.DoesNotExist: |
|---|
| 445 |
tag = Tag( group = group, |
|---|
| 446 |
name = tag_name_str ) |
|---|
| 447 |
tag.save() |
|---|
| 448 |
|
|---|
| 449 |
tag_label = TagLabel( tag = tag, |
|---|
| 450 |
label = tag_label_str ) |
|---|
| 451 |
tag_label.save() |
|---|
| 452 |
|
|---|
| 453 |
return tag_label |
|---|
| 454 |
|
|---|
| 455 |
|
|---|
| 456 |
qn = connection.ops.quote_name |
|---|
| 457 |
|
|---|
| 458 |
def get_queryset_and_model(queryset_or_model): |
|---|
| 459 |
try: |
|---|
| 460 |
return queryset_or_model, queryset_or_model.model |
|---|
| 461 |
except AttributeError: |
|---|
| 462 |
return queryset_or_model._default_manager.all(), queryset_or_model |
|---|
| 463 |
|
|---|
| 464 |
def tag_get_models_by_tag(queryset_or_model, tag): |
|---|
| 465 |
|
|---|
| 466 |
queryset, model = get_queryset_and_model(queryset_or_model) |
|---|
| 467 |
content_type = ContentType.objects.get_for_model(model) |
|---|
| 468 |
opts = TaggedItem._meta |
|---|
| 469 |
tagged_item_table = qn(opts.db_table) |
|---|
| 470 |
tag_label_table = qn(TagLabel._meta.db_table) |
|---|
| 471 |
return queryset.extra( |
|---|
| 472 |
tables=[TaggedItem._meta.db_table, |
|---|
| 473 |
TagLabel._meta.db_table, ], |
|---|
| 474 |
where=[ |
|---|
| 475 |
'%s.content_type_id = %%s' % tagged_item_table, |
|---|
| 476 |
'%s.tag_id = %%s' % tag_label_table, |
|---|
| 477 |
'%s.tag_label_id = %s.%s' % (tagged_item_table, |
|---|
| 478 |
tag_label_table, |
|---|
| 479 |
TagLabel._meta.pk.column), |
|---|
| 480 |
'%s.%s = %s.object_id' % (qn(model._meta.db_table), |
|---|
| 481 |
qn(model._meta.pk.column), |
|---|
| 482 |
tagged_item_table) |
|---|
| 483 |
], |
|---|
| 484 |
params=[content_type.pk, tag.pk], |
|---|
| 485 |
) |
|---|
| 486 |
|
|---|
| 487 |
class Tag(models.Model): |
|---|
| 488 |
""" |
|---|
| 489 |
A tag is the internal representation which is always linked to a specific group. |
|---|
| 490 |
|
|---|
| 491 |
A tag name only allows alpha numeric characters without spaces, etc. and only stores |
|---|
| 492 |
lower case letters ! |
|---|
| 493 |
""" |
|---|
| 494 |
group = models.ForeignKey( Group ) |
|---|
| 495 |
name = models.CharField( max_length = 250, ) |
|---|
| 496 |
|
|---|
| 497 |
def __unicode__(self): |
|---|
| 498 |
return self.name |
|---|
| 499 |
|
|---|
| 500 |
class Meta: |
|---|
| 501 |
unique_together = (("group", "name")) |
|---|
| 502 |
|
|---|
| 503 |
class TagLabel(models.Model): |
|---|
| 504 |
""" |
|---|
| 505 |
A tag label represents the user entered value for the tag. Including uppercase/lowercase, |
|---|
| 506 |
and all characters usually not allowed within a tag. |
|---|
| 507 |
""" |
|---|
| 508 |
tag = models.ForeignKey(Tag, related_name = 'labels') |
|---|
| 509 |
label = models.CharField( max_length = 250, ) |
|---|
| 510 |
|
|---|
| 511 |
def __unicode__(self): |
|---|
| 512 |
return self.label |
|---|
| 513 |
|
|---|
| 514 |
class Meta: |
|---|
| 515 |
unique_together = (("tag", "label")) |
|---|
| 516 |
|
|---|
| 517 |
class TaggedItem(models.Model): |
|---|
| 518 |
""" |
|---|
| 519 |
Relationship between a tag label and an item. |
|---|
| 520 |
""" |
|---|
| 521 |
tag_label = models.ForeignKey(TagLabel, related_name = 'items') |
|---|
| 522 |
content_type = models.ForeignKey(ContentType, related_name = 'sph_taggeditem_set') |
|---|
| 523 |
object_id = models.PositiveIntegerField(db_index=True) |
|---|
| 524 |
object = generic.GenericForeignKey('content_type', 'object_id') |
|---|
| 525 |
|
|---|
| 526 |
class Meta: |
|---|
| 527 |
unique_together = (('tag_label', 'content_type', 'object_id')) |
|---|
| 528 |
|
|---|
| 529 |
|
|---|
| 530 |
|
|---|
| 531 |
|
|---|
| 532 |
|
|---|
| 533 |
|
|---|
| 534 |
from django import forms |
|---|
| 535 |
from sphene.community.forms import EditProfileForm, Separator |
|---|
| 536 |
from sphene.community.signals import profile_edit_init_form, profile_edit_save_form, profile_display |
|---|
| 537 |
|
|---|
| 538 |
|
|---|
| 539 |
def get_public_emailaddress_help(): |
|---|
| 540 |
|
|---|
| 541 |
if get_sph_setting( 'community_email_show_only_public' ): |
|---|
| 542 |
return _('This email address will be shown to all users. If you leave it black noone will see your email address.') |
|---|
| 543 |
return _('This email address will be shown to all users. If you leave it blank, your verified email address will be shown.') |
|---|
| 544 |
|
|---|
| 545 |
def get_user_displayname_help(): |
|---|
| 546 |
if get_sph_setting( 'community_user_displayname_fallback' ) == 'username': |
|---|
| 547 |
return _('This display name will be shown to all users. If you leave it blank then your username will be shown.') |
|---|
| 548 |
return _('This display name will be shown to all users. If you leave it blank, your first and last name will be shown. If those are blank too, then your username will be shown.') |
|---|
| 549 |
|
|---|
| 550 |
|
|---|
| 551 |
def community_profile_edit_init_form(sender, instance, signal, request, *args, **kwargs): |
|---|
| 552 |
user = instance.user |
|---|
| 553 |
try: |
|---|
| 554 |
profile = CommunityUserProfile.objects.get( user = user, ) |
|---|
| 555 |
except CommunityUserProfile.DoesNotExist: |
|---|
| 556 |
profile = CommunityUserProfile( user = user, ) |
|---|
| 557 |
|
|---|
| 558 |
instance.fields['community_settings'] = Separator(label=_(u'Community settings')) |
|---|
| 559 |
instance.fields['displayname'] = forms.CharField( label = _(u'Display name'), |
|---|
| 560 |
required = False, |
|---|
| 561 |
initial = profile.displayname, |
|---|
| 562 |
help_text = get_user_displayname_help()) |
|---|
| 563 |
|
|---|
| 564 |
instance.fields['public_emailaddress'] = forms.CharField( label = _(u'Public email address'), |
|---|
| 565 |
required = False, |
|---|
| 566 |
initial = profile.public_emailaddress, |
|---|
| 567 |
help_text = get_public_emailaddress_help()) |
|---|
| 568 |
|
|---|
| 569 |
|
|---|
| 570 |
|
|---|
| 571 |
|
|---|
| 572 |
fields = CommunityUserProfileField.objects.all() |
|---|
| 573 |
for field in fields: |
|---|
| 574 |
initial = '' |
|---|
| 575 |
if profile.id: |
|---|
| 576 |
try: |
|---|
| 577 |
value = CommunityUserProfileFieldValue.objects.get( user_profile = profile, |
|---|
| 578 |
profile_field = field, ) |
|---|
| 579 |
initial = value.value |
|---|
| 580 |
except CommunityUserProfileFieldValue.DoesNotExist: |
|---|
| 581 |
pass |
|---|
| 582 |
|
|---|
| 583 |
instance.fields['community_field_%d' % field.id] = forms.RegexField( regex = field.regex, |
|---|
| 584 |
label = field.name, |
|---|
| 585 |
help_text = field.help_text, |
|---|
| 586 |
initial = initial, |
|---|
| 587 |
required = False, |
|---|
| 588 |
) |
|---|
| 589 |
|
|---|
| 590 |
def community_profile_edit_save_form(sender, instance, signal, request, *args, **kwargs): |
|---|
| 591 |
data = instance.cleaned_data |
|---|
| 592 |
user = instance.user |
|---|
| 593 |
try: |
|---|
| 594 |
profile = CommunityUserProfile.objects.get( user = user, ) |
|---|
| 595 |
except CommunityUserProfile.DoesNotExist: |
|---|
| 596 |
profile = CommunityUserProfile( user = user, ) |
|---|
| 597 |
|
|---|
| 598 |
profile.public_emailaddress = data['public_emailaddress'] |
|---|
| 599 |
profile.displayname = data['displayname'] |
|---|
| 600 |
profile.save() |
|---|
| 601 |
|
|---|
| 602 |
fields = CommunityUserProfileField.objects.all() |
|---|
| 603 |
for field in fields: |
|---|
| 604 |
try: |
|---|
| 605 |
value = CommunityUserProfileFieldValue.objects.get( user_profile = profile, |
|---|
| 606 |
profile_field = field, ) |
|---|
| 607 |
initial = value.value |
|---|
| 608 |
except CommunityUserProfileFieldValue.DoesNotExist: |
|---|
| 609 |
value = CommunityUserProfileFieldValue( user_profile = profile, |
|---|
| 610 |
profile_field = field, ) |
|---|
| 611 |
newvalue = data['community_field_%d' % field.id] |
|---|
| 612 |
if newvalue: |
|---|
| 613 |
value.value = data['community_field_%d' % field.id] |
|---|
| 614 |
value.save() |
|---|
| 615 |
else: |
|---|
| 616 |
if value.id: value.delete() |
|---|
| 617 |
|
|---|
| 618 |
request.user.message_set.create( message = _("Successfully saved community profile.") ) |
|---|
| 619 |
|
|---|
| 620 |
def community_profile_display(sender, signal, request, user, **kwargs): |
|---|
| 621 |
try: |
|---|
| 622 |
profile = CommunityUserProfile.objects.get( user = user, ) |
|---|
| 623 |
except CommunityUserProfile.DoesNotExist: |
|---|
| 624 |
return None |
|---|
| 625 |
|
|---|
| 626 |
ret = '' |
|---|
| 627 |
fields = CommunityUserProfileField.objects.all() |
|---|
| 628 |
for field in fields: |
|---|
| 629 |
try: |
|---|
| 630 |
value = CommunityUserProfileFieldValue.objects.get( user_profile = profile, |
|---|
| 631 |
profile_field = field, ) |
|---|
| 632 |
formatstring = '<tr><th>%(label)s</th><td>' + (field.renderstring or '%(value)s') + '</td></tr>' |
|---|
| 633 |
ret += (formatstring % { 'label': field.name, |
|---|
| 634 |
'value': value.value, }) |
|---|
| 635 |
except CommunityUserProfileFieldValue.DoesNotExist: |
|---|
| 636 |
continue |
|---|
| 637 |
|
|---|
| 638 |
return ret |
|---|
| 639 |
|
|---|
| 640 |
profile_edit_init_form.connect(community_profile_edit_init_form, sender = EditProfileForm) |
|---|
| 641 |
profile_edit_save_form.connect(community_profile_edit_save_form, sender = EditProfileForm) |
|---|
| 642 |
profile_display.connect(community_profile_display) |
|---|
| 643 |
|
|---|
| 644 |
|
|---|