summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coip/apps/membership/models.py5
-rw-r--r--coip/apps/tag/views.py27
-rw-r--r--coip/settings.py1
-rw-r--r--coip/urls.py2
-rw-r--r--site-media/css/jquery.ui.autocomplete.custom.css75
-rw-r--r--site-media/js/tag-it.js100
-rw-r--r--templates/apps/name/name.html4
-rw-r--r--templates/apps/tag/add.html34
-rw-r--r--templates/base.html2
9 files changed, 248 insertions, 2 deletions
diff --git a/coip/apps/membership/models.py b/coip/apps/membership/models.py
index 0dc06ca..324c736 100644
--- a/coip/apps/membership/models.py
+++ b/coip/apps/membership/models.py
@@ -10,6 +10,7 @@ import datetime
from pprint import pformat
import logging
from coip.apps.entity.models import Entity
+import tagging
class Membership(models.Model):
'''
@@ -74,4 +75,6 @@ def has_member(name,member_name):
if isinstance(member_name,User):
return Membership.objects.filter(name=name,user=member_name)
else:
- return Membership.objects.filter(name=name,entity=member_name) \ No newline at end of file
+ return Membership.objects.filter(name=name,entity=member_name)
+
+tagging.register(Membership) \ No newline at end of file
diff --git a/coip/apps/tag/views.py b/coip/apps/tag/views.py
index 60f00ef..a08d696 100644
--- a/coip/apps/tag/views.py
+++ b/coip/apps/tag/views.py
@@ -1 +1,26 @@
-# Create your views here.
+'''
+Created on Mar 3, 2011
+
+@author: jbn
+'''
+from django.core.exceptions import ObjectDoesNotExist
+from django.http import HttpResponse, HttpResponseNotFound, HttpResponseForbidden, HttpResponseRedirect, Http404
+from django.contrib.auth.decorators import login_required
+from coip.multiresponse import respond_to, json_response, render403
+import logging
+from django.shortcuts import get_object_or_404
+from django.contrib.auth.models import User
+from coip.apps.membership.models import Membership
+from forms import *
+from tagging.models import Tag
+
+def add(request, type, id):
+ if type == "membership":
+ tagobj = get_object_or_404(Membership, pk=id)
+ else: return HttpResponseNotFound()
+ if request.method == 'POST':
+ if tagobj.user == request.user:
+ for tag in request.POST.getlist('item[tags][]'):
+ Tag.objects.add_tag(tagobj, tag)
+ return HttpResponseRedirect(request.META["HTTP_REFERER"])
+ return respond_to(request,{'text/html': 'apps/tag/add.html'},{'tagobj': tagobj, 'type': type, 'name': tagobj.name}) \ No newline at end of file
diff --git a/coip/settings.py b/coip/settings.py
index 7ef7c13..575a6c7 100644
--- a/coip/settings.py
+++ b/coip/settings.py
@@ -83,6 +83,7 @@ INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.humanize',
'django_extensions',
+ 'tagging',
'coip.extensions',
'coip.apps.name',
'coip.apps.membership',
diff --git a/coip/urls.py b/coip/urls.py
index 4535785..2b30391 100644
--- a/coip/urls.py
+++ b/coip/urls.py
@@ -55,6 +55,8 @@ urlpatterns = patterns('',
(r'^link/(?P<id>[0-9]+)/remove$', 'coip.apps.link.views.remove'),
# Membership
(r'^membership/(?P<id>[0-9]+)$', 'coip.apps.membership.views.show'),
+ # Tag
+ (r'^tag/(?P<type>(\S+))/(?P<id>[0-9]+)/add$', 'coip.apps.tag.views.add'),
# JSON Tree
(r'^ctree.json$', 'coip.apps.name.views.ctree'),
(r'^ctree/(?P<id>[0-9]+).json$', 'coip.apps.name.views.ctree'),
diff --git a/site-media/css/jquery.ui.autocomplete.custom.css b/site-media/css/jquery.ui.autocomplete.custom.css
new file mode 100644
index 0000000..33c4084
--- /dev/null
+++ b/site-media/css/jquery.ui.autocomplete.custom.css
@@ -0,0 +1,75 @@
+.ui-autocomplete {
+ background-color: #eee;
+ position: absolute;
+ cursor: default;
+}
+.ui-autocomplete .ui-menu-item {
+}
+.ui-autocomplete .ui-menu-item a {
+ display:block;
+ padding:4px 6px;
+ text-decoration:none;
+ line-height:12px;
+}
+.ui-autocomplete .ui-menu-item a.ui-state-hover,
+.ui-autocomplete .ui-menu-item a.ui-state-active {
+ background-color:#78959D;
+ color:#fff;
+ margin:0;
+}
+.ui-autocomplete-loading {
+ background: white url(images/ui-anim_basic_16x16.gif) right center no-repeat;
+}
+
+ul.tagit {
+ padding:1px 5px;
+ border-style:solid;
+ border-width:1px;
+ border-color:#C6C6C6;
+ overflow:auto;
+}
+ul.tagit li {
+ -moz-border-radius:5px 5px 5px 5px;
+ display: block;
+ float: left;
+ margin:2px 5px 2px 0;
+}
+ul.tagit li.tagit-choice {
+ background-color:#DEE7F8;
+ border:1px solid #CAD8F3;
+ padding:2px 4px 3px;
+}
+ul.tagit li.tagit-choice:hover {
+ background-color:#bbcef1;
+ border-color:#6d95e0;
+}
+ul.tagit li.tagit-new {
+ padding:2px 4px 3px;
+ padding:2px 4px 1px;
+ padding:2px 4px 1px 0;
+}
+
+ul.tagit li.tagit-choice input {
+ display:block;
+ float:left;
+ margin:2px 5px 2px 0;
+}
+ul.tagit li.tagit-choice a.close {
+ color:#777777;
+ cursor:pointer;
+ font-size:12px;
+ font-weight:bold;
+ outline:medium none;
+ padding:2px 0 2px 3px;
+ text-decoration:none;
+}
+ul.tagit input[type="text"] {
+ -moz-box-sizing:border-box;
+ border:none;
+ margin:0;
+ padding:0;
+ width:inherit;
+ border-color:#C6C6C6;
+ background-color:#FFFFFF;
+ color:#333333;
+}
diff --git a/site-media/js/tag-it.js b/site-media/js/tag-it.js
new file mode 100644
index 0000000..040cef4
--- /dev/null
+++ b/site-media/js/tag-it.js
@@ -0,0 +1,100 @@
+(function($) {
+
+ $.fn.tagit = function(options) {
+
+ var el = this;
+
+ const BACKSPACE = 8;
+ const ENTER = 13;
+ const SPACE = 32;
+ const COMMA = 44;
+
+ // add the tagit CSS class.
+ el.addClass("tagit");
+
+ // create the input field.
+ var html_input_field = "<li class=\"tagit-new\"><input class=\"tagit-input\" type=\"text\" /></li>\n";
+ el.html (html_input_field);
+
+ tag_input = el.children(".tagit-new").children(".tagit-input");
+
+ $(this).click(function(e){
+ if (e.target.tagName == 'A') {
+ // Removes a tag when the little 'x' is clicked.
+ // Event is binded to the UL, otherwise a new tag (LI > A) wouldn't have this event attached to it.
+ $(e.target).parent().remove();
+ }
+ else {
+ // Sets the focus() to the input field, if the user clicks anywhere inside the UL.
+ // This is needed because the input field needs to be of a small size.
+ tag_input.focus();
+ }
+ });
+
+ tag_input.keypress(function(event){
+ if (event.which == BACKSPACE) {
+ if (tag_input.val() == "") {
+ // When backspace is pressed, the last tag is deleted.
+ $(el).children(".tagit-choice:last").remove();
+ }
+ }
+ // Comma/Space/Enter are all valid delimiters for new tags.
+ else if (event.which == COMMA || event.which == SPACE || event.which == ENTER) {
+ event.preventDefault();
+
+ var typed = tag_input.val();
+ typed = typed.replace(/,+$/,"");
+ typed = typed.trim();
+
+ if (typed != "") {
+ if (is_new (typed)) {
+ create_choice (typed);
+ }
+ // Cleaning the input.
+ tag_input.val("");
+ }
+ }
+ });
+
+ tag_input.autocomplete({
+ source: options.availableTags,
+ select: function(event,ui){
+ if (is_new (ui.item.value)) {
+ create_choice (ui.item.value);
+ }
+ // Cleaning the input.
+ tag_input.val("");
+
+ // Preventing the tag input to be update with the chosen value.
+ return false;
+ }
+ });
+
+ function is_new (value){
+ var is_new = true;
+ this.tag_input.parents("ul").children(".tagit-choice").each(function(i){
+ n = $(this).children("input").val();
+ if (value == n) {
+ is_new = false;
+ }
+ })
+ return is_new;
+ }
+ function create_choice (value){
+ var el = "";
+ el = "<li class=\"tagit-choice\">\n";
+ el += value + "\n";
+ el += "<a class=\"close\">x</a>\n";
+ el += "<input type=\"hidden\" style=\"display:none;\" value=\""+value+"\" name=\"item[tags][]\">\n";
+ el += "</li>\n";
+ var li_search_tags = this.tag_input.parent();
+ $(el).insertBefore (li_search_tags);
+ this.tag_input.val("");
+ }
+ };
+
+ String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g,"");
+ };
+
+})(jQuery);
diff --git a/templates/apps/name/name.html b/templates/apps/name/name.html
index 32dad82..e00a887 100644
--- a/templates/apps/name/name.html
+++ b/templates/apps/name/name.html
@@ -1,6 +1,7 @@
{% extends "tree.html" %}
{% load datehumanize %}
{% load userdisplay %}
+{% load tagging_tags %}
{% block widgets %}
$("#memberships").accordion({
header: 'h3',
@@ -62,7 +63,10 @@
<div class="rlist">
<ul>
{% if user == m.user %}
+ <b>Role{{ m.tags|pluralize }} in this group</b><br>
+ {% for tag in m.tags %}<ul>{{ tag|escape }}</ul>{% endfor %}
<li class="button"><a href="/name/{{name.id}}/leave/{{m.user.username}}">Leave Group</a></li>
+ <li class="button"><a href="/tag/membership/{{m.id}}/add">Add role</a></li>
{% else %}
{% if render.kick %}
<li class="button"><a href="/name/{{name.id}}/leave/{{m.user.username}}">Remove from Group</a></li>
diff --git a/templates/apps/tag/add.html b/templates/apps/tag/add.html
new file mode 100644
index 0000000..1325929
--- /dev/null
+++ b/templates/apps/tag/add.html
@@ -0,0 +1,34 @@
+{% extends "tree.html" %}
+{% block js %}
+<script>
+ $(document).ready(function(){
+ $("#taglist").tagit({
+ existingTags: [{% for t in tagobj.tags %}'{{t}}',{% endfor%}],
+ namePrefix: 'tags'
+ });
+ });
+</script>
+{% endblock %}
+
+{% block content %}
+ <h1>Add role to {{ type|escape }} {{ name }}</h1>
+ <div class="ui-widget ui-widget-content ui-helper-reset ui-corner-all infopanel">
+ <h3 class="altheader">Current role{{ tagobj.tags|pluralize }} in this group</h3>
+ {% for tag in tagobj.tags %}<b>{{ tag|escape }} </b>{% endfor %}
+ <h3>Add</h3>
+ <div class="altcontent">
+ <form method="POST" action="">{% csrf_token %}
+ <div style="margin-top: 20px;">
+ <ul id="taglist"></ul>
+ </div>
+ <br/>
+ <div class="button">
+ <input type="submit" value="Save Changes" />
+ <input type="button" onClick="history.go(-1)" value="Cancel"/>
+ </div>
+ </form>
+ </div>
+ </div>
+
+
+{% endblock %} \ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 4f93552..fddbc95 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -6,6 +6,7 @@
<link type="text/css" href="/site-media/css/jquery.tooltip.css" rel="stylesheet" />
<link type="text/css" href="/site-media/css/jquery.tagInput.css" rel="stylesheet" />
<link type="text/css" href="/site-media/css/jquery.multiselect2side.css" rel="stylesheet" /-->
+ <link type="text/css" href="/site-media/css/jquery.ui.autocomplete.custom.css" rel="stylesheet" />
<link type="text/css" href="/site-media/css/jquery.wysiwyg.css" rel="stylesheet" />
<link type="text/css" href="/site-media/css/style.css" rel="stylesheet"/>
<script type="text/javascript" src="/site-media/js/jquery-1.4.4.min.js"></script>
@@ -24,6 +25,7 @@
<script type="text/javascript" src="/site-media/js/jquery.validate.js"></script>
<script type="text/javascript" src="/site-media/js/bbq.js"></script>
<script type="text/javascript" src="/site-media/js/jquery.form.wizard-3.0.4.js"></script>
+ <script type="text/javascript" src="/site-media/js/tag-it.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{% block title %}{% endblock %}</title>
<script type="text/javascript">