admin.site.register(Character, CharacterAdmin)
class InfluenceAdmin(admin.ModelAdmin):
- list_filter=('character', 'status', 'longterm', 'todo')
- search_fields=('character', 'summary', 'description', 'contact')
- list_display=('character', 'contact', 'summary', 'longterm', 'status')
+ list_filter=('initiator', 'status', 'longterm', 'todo')
+ search_fields=('initiator', 'summary', 'description', 'contact')
+ list_display=('initiator', 'summary', 'longterm', 'status')
class Media:
js = ('base/js/yahoo-dom-event.js', 'base/js/logger-debug.js')
class InfluenceForm(ContextModelForm):
class Meta:
model = Influence
- fields = ('character', 'contact', 'summary', 'description')
+ fields = ('initiator', 'summary', 'other_characters', 'other_contacts', 'description')
class CharacterForm(ContextModelForm):
class Meta:
from django.utils.translation import ugettext_lazy as _
from threadedcomments.models import ThreadedComment
from xerxes.tools.text import rewrap
+from string import strip
# Create your models here.
class Character(models.Model):
('N', _('New')),
('A', _('Approved')),
)
+ TYPE_CHOICES = (
+ ('P', _('Player')),
+ ('N', _('NPC')),
+ ('C', _('Contact')),
+ )
created = models.DateField(auto_now_add=1, verbose_name = _("Creation time"))
modified = models.DateField(auto_now=1, verbose_name = _("Modification time"))
name = models.CharField(max_length=255, verbose_name = _("Name"))
status = models.CharField(max_length=2, choices=STATUS_CHOICES, default='N', verbose_name = _("Status"))
player = models.ForeignKey(User, verbose_name = _("Player"))
+ contacts = models.ManyToManyField('self')
+ type = models.CharField(max_length=2, choices=TYPE_CHOICES, verbose_name=_("Type"))
def __unicode__(self):
return self.name
created = models.DateField(auto_now_add=1, verbose_name = _("Creation time"))
modified = models.DateField(auto_now=1, verbose_name = _("Modification time"))
- character = models.ForeignKey(Character, verbose_name = _("Character"))
- contact = models.CharField(max_length=255, verbose_name = _("Contact Name"))
+ initiator = models.ForeignKey(Character, verbose_name = _("Initiator"), related_name='initiated_influences')
+ other_contacts = models.CharField(max_length=255, blank = True, verbose_name = _("Other Contacts"))
+ other_characters = models.ManyToManyField(Character, blank = True, verbose_name = _("Involved characters"), related_name='influences_involved_in')
summary = models.CharField(max_length=255, verbose_name = _("Summary"))
description = models.TextField(verbose_name = _("Description"))
todo = models.TextField(blank=True, verbose_name = _("Todo"))
prefix=prefix)
return comments
+ @property
+ def involved(self):
+ """ Returns the Characters and contacts (strings) involved """
+ chars = list(self.other_characters.all())
+ if (self.other_contacts):
+ chars.extend(map(strip,self.other_contacts.split(',')))
+ return chars
+
+ @property
+ def related_players(self):
+ """ Returns all players to this Influence (ie, the players of the
+ initiator or involved characters). Returns a dict where the
+ players (User objects) are keys and a list of Character objects
+ for which this player is related is the value.
+ """
+ players = {self.initiator.player : [self.initiator]}
+ for char in self.other_characters.all():
+ # Add this character to the player's list of characters for
+ # this Influence, creating a new list if this is the first
+ # character.
+ chars = players.get(char.player, [])
+ chars.append(char)
+ players[char.player] = chars
+ return players
+
class Meta:
verbose_name = _("Influence")
verbose_name_plural = _("Influences")
# If a character_id was specified in the url, or there is only one
# character, preselect it.
if (character_id):
- initial['character'] = character_id
+ initial['initiator'] = character_id
elif (chars.count() == 1):
- initial['character'] = chars[0].id
+ initial['initiator'] = chars[0].id
f = InfluenceForm(request=request, initial=initial)
# Only allow characters of the current user. Putting this here also
# ensures that a form will not validate when any other choice was
# selected (perhaps through URL crafting).
- f.fields['character']._set_queryset(chars)
+ f.fields['initiator']._set_queryset(chars)
if (f.is_valid()):
# The form was submitted, let's save it.
def index(request):
# Only show this player's characters and influences
characters = request.user.character_set.all()
- influences = Influence.objects.filter(character__player=request.user)
+ influences = Influence.objects.filter(initiator__player=request.user)
return render_to_response('influences/index.html', {'characters' : characters, 'influences' : influences}, RequestContext(request))
#
@login_required
def influence_list(request):
# Only show this player's influences
- os = Influence.objects.filter(character__player=request.user)
+ os = Influence.objects.filter(initiator__player=request.user)
return render_to_response('influences/influence_list.html', {'object_list' : os}, RequestContext(request))
def influence_comment_preview(request, context_processors, extra_context, **kwargs):
o = Influence.objects.get(pk=object_id)
# Don't show other player's influences
- if (not request.user.is_staff and o.character.player != request.user):
- return HttpResponseForbidden("Forbidden -- Trying to view influences of somebody else's character")
+ if (not request.user.is_staff and not request.user in o.related_players):
+ return HttpResponseForbidden("Forbidden -- Trying to view influences you are not involved in.")
# Show all comments to staff, but only public comments to other
# users
# Max length for comments, in characters.
DEFAULT_MAX_COMMENT_LENGTH = 3000
+INTERNAL_IPS = ('127.0.0.1')
+
# Import local settings, that are specific to this installation. These
# can override any settings specified here.
try:
{% extends "base/base.html" %}
{% load i18n %}
+{% load list %}
+{% load misc %}
{% block content %}
<h1>{{ object.summary }}</h1>
<table>
-<tr><td>{% trans "Contact" %}:</td><td>{{ object.contact }}</td></tr>
-<tr><td>{% trans "Character" %}:</td><td><a href="{% url influences_character_detail object.character.id %}">{{ object.character }}</a></td></tr>
+<tr><td>{% trans "Iniator" %}:</td><td><a href="{% url influences_character_detail object.initiator.id %}">{{ object.initiator }}</a></td></tr>
+{% if object.involved %}
+<tr><td>{% trans "Involved" %}:</td><td>
+{{ object.involved|list_or_value }}</td></tr>
+{% endif %}
{% if object.longterm %}
<tr><td>{% trans "Long term" %}:</td><td>{{ object.longterm|yesno|capfirst }}</td></tr>
{% endif %}
</table>
<p>{{ object.description }}</p>
-
+{# Show all related players, except for the current user #}
+{% with object.related_players|remove_item:user as players %}
+ {% if players %}
+ {% trans "Note: This influence (and its comments) can also be viewed by:" %}
+ <ul>
+ {% for player, chars in players.items %}
+ <li>{{ player }} ({% trans "player of" %} {{ chars|natural_list }})</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+{% endwith %}
<h2>{% trans "Comments" %}</h2>
{% block comments %}
{% include "influences/influence_comments_block.html" %}
--- /dev/null
+from django import template
+from django.template.defaultfilters import unordered_list
+from django.utils.safestring import mark_safe
+from django.utils.encoding import force_unicode
+from django.utils.translation import ugettext as _
+
+"""
+ Template tags and filters for working with lists.
+"""
+
+register = template.Library()
+
+@register.filter(name='list_or_value')
+def list_or_value(list, autoescape=None):
+ """
+ Turn a list into a simple string or unordered list.
+
+ If the list is empty, returns an empty string.
+ If the list contains one element, returns just that element.
+ If the list contains more elements, return an ordered list with
+ those elements (Just like the builtin unordered_list, but with the
+ <ul> tags).
+ """
+ if len(list) == 0:
+ return ''
+ elif len(list) == 1:
+ return list[0]
+ else:
+ return mark_safe('<ul>' + unordered_list(list, autoescape=autoescape) + '</ul>')
+list_or_value.needs_autoescape = True
+
+@register.filter(name='natural_list')
+def natural_list(list):
+ """
+ Turns the list into a natural list, using comma's and "and" for
+ joining the terms. The result is somewhat localized (but probably
+ insufficient for language that use completely different
+ interpunction for lists).
+ """
+ if len(list) == 0:
+ return ''
+ res = ''
+ for item in list[0:-1]:
+ if res:
+ res += ', '
+ res += item.__unicode__()
+
+ if res:
+ res += ' %s ' % _('and')
+
+ res += list[-1].__unicode__()
+
+ return res
+natural_list.is_safe = True
--- /dev/null
+from django import template
+
+"""
+ Miscellaneous template tags and filters.
+"""
+
+register = template.Library()
+@register.filter(name='remove_item')
+def remove_item(container, item):
+ """
+ Removes the given user from the filtered list or dict.
+ """
+ if (item in container):
+ if isinstance(container, list):
+ container.remove(item)
+ elif isinstance(container, dict):
+ container.pop(item)
+ return container
+
+# vim: set sts=4 sw=4 expandtab: