`# coding: utf-8
# vim: ai ts=4 sts=4 et sw=4
#from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
from django.conf.urls.defaults import *
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from django.conf import settings
from django.utils.encoding import force_unicode
from django.utils import simplejson
from django import template
from copy import deepcopy
class MpttAdmin(admin.ModelAdmin):
""" Base class for mptt model admins, usage:
class ExampleAdmin(MpttAdmin):
tree_title_field = 'name'
tree_display = ('name','slug','created|date') # you can use filters here
prepopulated_fields = {"slug": ("name",)}
class Meta:
model = Example
"""
def __init__(self,*args,**kargs):
super(MpttAdmin,self).__init__(*args,**kargs)
if not hasattr(self,'tree_display'):
self.tree_display = ()
if self.tree_display and not hasattr(self,'tree_title_field'):
self.tree_title_field = self.tree_display[0]
if not hasattr(self,'tree_title_field'):
title_field = ''#self.tree_display[0]
else:
title_field = '.'+self.tree_title_field
extra_fields = ' '.join('{{ node.%s }}' % (field,field) for field in self.tree_display if not hasattr(self,'tree_title_field') or field!=self.tree_title_field)
model = '%s.%s' % (self.Meta.model._meta.app_label, self.Meta.model._meta.object_name)
self._tree_tpl = template.Template("""{% load mptt_tags %}{% full_tree_for_model """+model+""" as nodes %}{% for node,structure in nodes|tree_info %}{% if structure.new_level %}{% if node.is_child_node %}
{% endif %}- {% else %}
- {% endif %} {{ node"""+title_field+""" }}"""+extra_fields+"""{% for level in structure.closed_levels %}
{% if node.is_child_node %}
{% endif %}{% endfor %}{% endfor %}""")
self._changelist_tpl = template.Template("""{% extends "admin/change_list.html" %}
{% load mptt_tags %}
{% block extrahead %}
{% endblock %}
{% block search %}{% endblock %}{% block date_hierarchy %}{% endblock %}
{% block result_list %}{% endblock %}{% block pagination %}{% endblock %}
{% block filters %}{% endblock %}""")
#self.move_node = permission_required('%s.change_%s' % (self.model._meta.app_label,self.model._meta.object_name))(self.move_node)
#self.rename = permission_required('%s.change_%s' % (self.model._meta.app_label,self.model._meta.object_name))(self.rename)
#self.remove = permission_required('%s.delete_%s' % (self.model._meta.app_label,self.model._meta.object_name))(self.remove)
def changelist_view(self, request, extra_context=None):
model = '%s.%s' % (self.Meta.model._meta.app_label, self.Meta.model._meta.object_name)
opts = self.model._meta
app_label = opts.app_label
media = deepcopy(self.media)
media.add_js(['js/lib/jquery-1.3.2.min.js',
'js/lib/jquery.tree.min.js',
'js/lib/plugins/jquery.tree.contextmenu.js'])
module_name = force_unicode(opts.verbose_name_plural)
permissions = simplejson.dumps({
'renameable' : self.has_change_permission(request, None) and hasattr(self,'tree_title_field'),
'deletable' : self.has_delete_permission(request, None),
'creatable' : self.has_add_permission(request),
'draggable' : self.has_change_permission(request, None),
})
context = {
'module_name': module_name,
'title': module_name,
'is_popup': False,
'cl': {'opts':{'verbose_name_plural': module_name}},
'media': media,
'has_add_permission': self.has_add_permission(request),
'root_path': self.admin_site.root_path,
'app_label': app_label,
'tree':self._tree_tpl.render(template.Context()),
'permissions': permissions,
'MEDIA_URL': settings.MEDIA_URL,
}
context.update(extra_context or {})
context_instance = template.RequestContext(request, current_app=self.admin_site.name)
context_instance.update(context)
return HttpResponse(self._changelist_tpl.render(context_instance))
def get_urls(self):
urls = super(MpttAdmin, self).get_urls()
my_urls = patterns('',
(r'^tree/$', self.get_tree),
(r'^move_node/$', self.move_node),
(r'^rename/$', self.rename),
(r'^remove/$', self.remove),
)
return my_urls + urls
def get_tree(self,request):
return HttpResponse(self._tree_tpl.render(template.Context()))
def move_node(self,request):
if not self.has_change_permission(request, None):
raise PermissionDenied
node = get_object_or_404(self.Meta.model,pk=request.POST.get('node'))
target = get_object_or_404(self.Meta.model,pk=request.POST.get('target'))
position = request.POST.get('position')
if position not in ('left','right','last-child','first-child'):
return HttpResponseBadRequest('bad position')
self.Meta.model.tree.move_node(node,target,position)
return self.get_tree(request)
def rename(self,request):
if not self.has_change_permission(request, None):
raise PermissionDenied
node = get_object_or_404(self.Meta.model,pk=request.POST.get('node'))
setattr(node,self.tree_title_field, request.POST.get('name'))
print self.tree_title_field, request.POST.get('name')
node.save()
return self.get_tree(request)
def remove(self,request):
if not self.has_delete_permission(request, None):
raise PermissionDenied
node = get_object_or_404(self.Meta.model,pk=request.POST.get('node'))
node.delete()
return self.get_tree(request)`
jstree_admin.js:
`$(document).ready(function() {
if (!$('#tree').length)
return false;
$(function() {
window.jtree = $("#tree").tree({
plugins: {
contextmenu: {
items: {
remove: true,
create: {
label: "Create",
icon: "create",
visible: function(node, treeobj) {
if (node.length != 1)
return 0;
return treeobj.check("creatable", node);
},
action: function(node, treeobj) {
location.href = 'add/?parent=' + node.attr('id').replace('n','');
},
separator_after: true
},
rename: {
label: "Rename",
icon: "rename",
visible: function(node, treeobj) {
if (node.length != 1)
return false;
return treeobj.check("renameable", node);
},
action: function(node, treeobj) {
treeobj.rename(node);
}
},
edit: {
label: "Change",
icon: "rename",
visible: function(node, treeobj) {
if (node.length != 1)
return false;
return true;
},
action: function(node, treeobj) {
location.href = $(node).attr('id').replace('n','') + '/';
}
},
remove: {
label: "Remove",
icon: "remove",
visible: function(node, treeobj) {
if (node.length != 1)
return false;
return treeobj.check("deletable", node);
},
action: function(node, treeobj) {
treeobj.remove(node);
}
}
}
}
},
callback: {
beforemove: function(node, ref_node, TYPE, treeobj) {
if(treeobj._moving){
treeobj._moving=false;
return true;
}else treeobj._moving=true;
var position={'inside':'last-child','before':'left','after':'right'}[TYPE];
treeobj.settings.data.opts.url = 'move_node/';
treeobj.settings.data.opts.method='POST';
treeobj._params={node:node.id.replace('n',''),target:ref_node.id.replace('n',''),position:position};
treeobj.refresh();
return false;
},
ondblclk: function(node, treeobj) {
location.href = $(node).attr('id').replace('n','');
},
onload: function(treeobj) {
treeobj.open_all();
},
onrename: function(node, treeobj, RB) {
//treeobj.settings.data._node=node;
var new_name=$(node).children('a:first').text();//.replace(/^\s\s*/, '');
treeobj._params={node:node.id.replace('n',''),name:new_name};
treeobj.settings.data.opts.url = 'rename/';
treeobj.settings.data.opts.method = 'POST';
treeobj.refresh();
return false;
},
beforedelete: function(node, treeobj) {
treeobj._params={node:node.id.replace('n','')};
treeobj.settings.data.opts.url = 'remove/';
treeobj.settings.data.opts.method = 'POST';
treeobj.refresh();
return false;
},
onsearch : function (n,t) {
t.container.find('.search').removeClass('search');
n.addClass('search');
},
ondata: function(data, treeobj) {
if(typeof(treeobj._params)!='undefined'){
treeobj._params={};
treeobj.settings.data.opts.url='tree/';
treeobj.settings.data.opts.method='POST';}
return data;
},
beforedata: function(node, treeobj) {
return typeof(treeobj._params)!='undefined' && treeobj._params || {};
},
onchange : function (node) {
document.location.href = $(node).children("a:eq(0)").attr("href");
}
},
data: {
type: "html",async: false
},
"types": {
"default": permissions
}
});
});
$('#changelist').removeClass('module');
});`