From 1fd9684064b3259cd2507aeeea81b4a4206b7004 Mon Sep 17 00:00:00 2001 From: Sanj Date: Tue, 1 Feb 2011 20:43:30 +0530 Subject: [PATCH] auto generate forms --- itf/api/views.py | 173 +++++++++++++++++++++++++++++++-- itf/app/models.py | 14 +++ itf/app/views.py | 4 +- itf/bestpractices/forms.py | 7 ++ itf/bestpractices/models.py | 9 +- itf/settings.py | 2 + itf/static/css/itf.css | 8 ++ itf/static/js/itf/app.js | 15 ++- itf/static/js/itf/construct.js | 71 +++----------- itf/static/js/itf/forms.js | 33 ++++--- itf/static/js/itf/itf.js | 29 ++++++ itf/static/js/itf/widgets.js | 151 +++++++++++++++++++++++++--- itf/urls.py | 2 + 13 files changed, 413 insertions(+), 105 deletions(-) create mode 100644 itf/bestpractices/forms.py diff --git a/itf/api/views.py b/itf/api/views.py index 5ede06b..5798d8f 100644 --- a/itf/api/views.py +++ b/itf/api/views.py @@ -17,14 +17,17 @@ from django.http import HttpResponse, Http404 from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect from django.template import RequestContext from django.conf import settings - +from django.contrib.comments.forms import CommentForm +from django.contrib.comments.models import Comment +from django.contrib.comments.signals import comment_will_be_posted, comment_was_posted +from django.contrib.contenttypes.models import ContentType from ox.utils import json from ox.django.decorators import login_required_json from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.http import HttpFileResponse import ox from actions import actions - +from user.models import get_user_json def api(request): if request.META['REQUEST_METHOD'] == "OPTIONS": @@ -84,11 +87,7 @@ def api(request): # return render_to_json_response(json_response({'actions': docs})) -#FIXME: REMOVE THIS FUNCTION WHEN THERE ARE REAL USERS!!!! -def get_user_json(u): - return {'name': 'Guest', 'group': 'guest', 'preferences': {}} - -def hello(request): +def init(request): ''' return {'status': {'code': int, 'text': string}, 'data': {user: object}} @@ -98,9 +97,15 @@ def hello(request): if request.user.is_authenticated(): response['data']['user'] = get_user_json(request.user) else: - response['data']['user'] = {'name': 'Guest', 'group': 'guest', 'preferences': {}} + response['data']['user'] = {'name': 'Guest', 'group': 'guest', 'level': 'guest', 'preferences': {}} +#FIXME: Get config definition out of here. + response['data']['config'] = { + 'site': { + 'name': 'India Theatre Forum' + } + } return render_to_json_response(response) -actions.register(hello) +actions.register(init) def error(request): ''' @@ -157,17 +162,159 @@ actions.register(preview) #FIXME: Generalize based on these two functions being the same. def info(request): + ''' + id: object id + model: string, name of model + module: string, name of module + ''' data = json.loads(request.POST['data']) id = int(data['id']) model = getModel(data) response = json_response({}) response['status'] = {'code': 200} obj = get_object_or_404(model, pk=id) + if model.hasComments: + response['commentForm'] = _get_comment_form_initial(request, obj) + response['comments'] = _get_comments(obj) response['data'] = obj.info_dict() response['template'] = getTemplate(data, "info") return render_to_json_response(response) actions.register(info) +#obj: Django model instance - object for which to get comment form for. +#Returns dict with content_type, object_id, security_hash, etc. +#FIXME: put comments stuff somewhere +def _get_comment_form_initial(request, obj): + c = CommentForm(obj) + return c.initial + +def _get_comments(obj): + ret = [] + content_type = ContentType.objects.get_for_model(obj) + object_id = obj.id + qset = Comment.objects.filter(content_type=content_type, object_pk=object_id) + for q in qset: + ret.append({ + 'name': q.name, +# 'date': q.submit_date, + 'comment': q.comment + }) + return ret + +@login_required_json +def addComment(request): + ''' + id + module + model + comment + content_type + email + name + object_pk + security_hash + timestamp + ''' + data = json.loads(request.POST['data']) + data.update({ + 'name': request.user.username, + 'email': request.user.email + }) + id = int(data['object_pk']) + model = getModel(data) + obj = get_object_or_404_json(model, pk=id) + cf = CommentForm(obj, data=data) + ret = json_response({}) + if cf.is_valid(): + comment = cf.get_comment_object() + comment.user = request.user + # Signal that the comment is about to be saved + responses = comment_will_be_posted.send( + sender = comment.__class__, + comment = comment, + request = request + ) + + for (receiver, response) in responses: + if response == False: + return CommentPostBadRequest( + "comment_will_be_posted receiver %r killed the comment" % receiver.__name__) + + # Save the comment and signal that it was saved + comment.save() + comment_was_posted.send( + sender = comment.__class__, + comment = comment, + request = request + ) + ret['data'] = { + id: comment.id + } + ret['status'] = {'code': 200} + + else: + ret['status'] = {'code': 200} + ret['errors'] = cf.errors + return render_to_json_response(ret) +actions.register(addComment) + +widgetMap = { + 'TextInput': 'text', + 'Textarea': 'textarea', + 'Select': 'select' +} + +def getAddItemForm(request): + response = json_response({}) + data = json.loads(request.POST['data']) + model = getModel(data) +# import pdb; pdb.set_trace() + form = getForm(data) + form_fields = [] + for field in form.base_fields: + name = field + this = form.base_fields[field] + widget = this.widget + widgetClassName = type(widget).__name__ + + if hasattr(widget, "choices"): + choices = [] + for c in widget.choices: + choices.append({ + 'id': c[0], + 'title': c[1] + }) + else: + choices = False + + js_widget = widgetMap.get(widgetClassName, "text") + form_fields.append({ + 'name': name, + 'widget': js_widget, + 'label': this.label, + 'choices': choices + }) + response['data']['form'] = form_fields + return render_to_json_response(response) +actions.register(getAddItemForm) + +def addItem(request): + response = json_response({}) + data = json.loads(request.POST['data']) + model = getModel(data) + form = getForm(data) + if form.is_valid(): + m = model() + for field in form.base_fields: + m.__setattr__(field, form.cleaned_data[field]) + m.save() + response['data'] = {'id': m.id} + else: + response['errors'] = form.errors + return render_to_json_response(response) + + +actions.register(addItem) def getTemplate(data, tmpl_name): path = join(settings.PROJECT_PATH, "templates", data['module'], data['model'], tmpl_name + ".html") @@ -177,6 +324,14 @@ def getModel(data): module = __import__(data['module']) return module.models.__getattribute__(data['model']) +#FIXME: BAAD Hack +def getForm(data): + module = __import__(data['module'] + ".forms") + model = getModel(data) + formStr = model.add_form + return module.forms.__getattribute__(formStr) + + def get_api_doc(f): f = 'api_' + f diff --git a/itf/app/models.py b/itf/app/models.py index fe00856..cbd9631 100644 --- a/itf/app/models.py +++ b/itf/app/models.py @@ -15,6 +15,7 @@ class ItfModel(models.Model): fts_fields = [] fk_filters = [] sort_fields = [] + hasComments = True class Meta: abstract = True @@ -110,3 +111,16 @@ def getField(fields, name): return f return False + + + +def site_config(): + with open(settings.SITE_CONFIG) as f: + site_config = json.load(f) + site_config['keys'] = {} + for key in site_config['itemKeys']: + site_config['keys'][key['id']] = key + site_config['_findKeys'] = {} + for key in site_config['findKeys']: + site_config['_findKeys'][key['id']] = key + return site_config diff --git a/itf/app/views.py b/itf/app/views.py index 9ea4157..37151be 100644 --- a/itf/app/views.py +++ b/itf/app/views.py @@ -13,6 +13,8 @@ def index(request): return html_snapshot(request) return render_to_response('index.html', context) + +''' def site_json(request): data = { 'site': { @@ -20,4 +22,4 @@ def site_json(request): } } return render_to_json_response(data) - +''' diff --git a/itf/bestpractices/forms.py b/itf/bestpractices/forms.py new file mode 100644 index 0000000..6786d61 --- /dev/null +++ b/itf/bestpractices/forms.py @@ -0,0 +1,7 @@ +from django.forms import ModelForm +from models import BestPractice + +class BestPracticeForm(ModelForm): + class Meta: + model = BestPractice + diff --git a/itf/bestpractices/models.py b/itf/bestpractices/models.py index 47ac110..004d4cd 100644 --- a/itf/bestpractices/models.py +++ b/itf/bestpractices/models.py @@ -2,6 +2,8 @@ from django.db import models from tagging.fields import TagField from django.core.paginator import Paginator, InvalidPage, EmptyPage from app.models import ItfModel +# from django.forms import +# import forms class BestPractice(ItfModel): title = models.CharField(max_length=512) @@ -12,13 +14,14 @@ class BestPractice(ItfModel): theatre = models.TextField(blank=True, help_text="Spotlight on Theatre text") quick_howto = models.TextField(blank=True) tags = TagField(blank=True, help_text="Enter as many tags as you like, separated by commas.") - category = models.ForeignKey("BestPracticeCategory") + category = models.ForeignKey("BestPracticeCategory", null=True) added = models.DateTimeField(auto_now_add=True, null=True) modified = models.DateTimeField(auto_now=True, null=True) fts_fields = ['title', 'story', 'guideline', 'law', 'theatre', 'quick_howto'] fk_filters = ['category'] sort_fields = ['title'] + add_form = "BestPracticeForm" def __unicode__(self): return str(self.id) + ": " + self.title @@ -103,3 +106,7 @@ class Glossary(models.Model): def __unicode__(self): return self.term + + + + diff --git a/itf/settings.py b/itf/settings.py index 58433fa..0ac167e 100644 --- a/itf/settings.py +++ b/itf/settings.py @@ -16,6 +16,7 @@ LOGGING_OUTPUT_ENABLED = True PROJECT_PATH = os.path.dirname(__file__) PROJECT_ROOT = PROJECT_PATH +SITE_CONFIG = join(PROJECT_ROOT, 'itf.json') CKEDITOR_MEDIA_PREFIX = "/static/ckeditor/" CKEDITOR_UPLOAD_PATH = join(PROJECT_PATH, "static/upload/images/") @@ -137,6 +138,7 @@ INSTALLED_APPS = ( 'debug_toolbar', 'sorl.thumbnail', 'south', + 'user', 'ckeditor', ) diff --git a/itf/static/css/itf.css b/itf/static/css/itf.css index aba220b..5817fef 100644 --- a/itf/static/css/itf.css +++ b/itf/static/css/itf.css @@ -10,3 +10,11 @@ .itfPreviewTitle { font-weight: bold; } + +.OxCommentWrapper { + margin-bottom: 4px; +} + +.OxCommentsWrapper { + margin-top: 6px; +} diff --git a/itf/static/js/itf/app.js b/itf/static/js/itf/app.js index b6fab32..eccdbd7 100644 --- a/itf/static/js/itf/app.js +++ b/itf/static/js/itf/app.js @@ -1,21 +1,16 @@ var app = new Ox.App({ apiURL: '/api/', - init: 'hello', + init: 'init', config: 'site.json' //FIXME: shouldn't need this, get data with 'hello' or 'init'. }); -/* -app.api.hell = function() { - setTimeout("app.api.hello()", 1000); -} -*/ app.launch(function(data) { Ox.theme("classic"); Ox.print(data); - + app.$body = $('body'); app.$document = $(document); - app.$window = $(window); + app.$window = $(window); app.$window.resize(function() { ITF.setSizes(); }); @@ -37,6 +32,10 @@ app.launch(function(data) { */ var wrapper = app.construct.wrapper(); app.$body.css({'opacity': 0}); + //FIXME: user handling should be cleaner? + if (data.user.level != 'guest') { + ITF.login(data); + } wrapper.appendTo(app.$body); ITF.setSizes(); app.$body.animate({ diff --git a/itf/static/js/itf/construct.js b/itf/static/js/itf/construct.js index 6b32ec1..1f6d390 100644 --- a/itf/static/js/itf/construct.js +++ b/itf/static/js/itf/construct.js @@ -472,9 +472,12 @@ BEGIN mainPanel id: id }); - var btnsWrapper = new Ox.Element().css({'textAlign': 'center', 'marginTop': '4px'}); - var loginBtn = new Ox.Button({ + var btnsWrapper = c.$btns = new Ox.Element().css({'textAlign': 'center', 'marginTop': '4px'}); + + var info = c.$info = new Ox.Element("span").appendTo(btnsWrapper); + + var loginBtn = c.$loginBtn = new Ox.Button({ 'id': 'loginBtn', 'title': 'Login', 'size': 'large' @@ -482,7 +485,7 @@ BEGIN mainPanel ui.accountDialog("login").open(); }).appendTo(btnsWrapper); - var registerBtn = new Ox.Button({ + var registerBtn = c.$registerBtn = new Ox.Button({ 'id': 'registerBtn', 'title': 'Register', 'size': 'large' @@ -490,62 +493,18 @@ BEGIN mainPanel ui.accountDialog("register").open(); }).appendTo(btnsWrapper); - btnsWrapper.appendTo(c); -/* - var registerForm = (function() { - var u = ui.accountDialogOptions('register'); - var btns = u.buttons; - var content = u.content; - var title = u.title; - // debugger; - var e = new Ox.Element().html(title).append(content); - var btnsWrapper = new Ox.Element().css({'textAlign': 'center', 'marginTop': '4px'}); - var regBtn = btns[1][1].appendTo(btnsWrapper); - var loginBtn = new Ox.Button({ - id: 'loginBtn', - title: 'Login' - }).bindEvent("click", function() { - // app.$ui.accountDialog = new Ox.Element(); - var l = ui.accountDialogOptions("login"); - var loginContent = l.content; - var loginSubmitBtn = l.buttons[1][1]; - var loginTitle = l.title; - var d = new Ox.Dialog({ - id: 'loginDialog', - content: loginContent, - buttons: [ - new Ox.Button({ - id: 'cancel', - title: 'Cancel', - }).bindEvent("click", function() { d.close(); }), - loginSubmitBtn - ], - title: loginTitle - }); - d.open(); + var logoutBtn = c.$logoutBtn = new Ox.Button({ + 'id': 'logoutBtn', + 'title': 'Logout', + 'size': 'large' + }).bindEvent("click", function() { + app.api.logout({}, function(data) { + ui.accountLogoutDialog().open(); }); - loginBtn.appendTo(btnsWrapper); - btnsWrapper.appendTo(e); - return e; - })(); -*/ + }).appendTo(btnsWrapper).hide(); -// registerForm.appendTo(c); -/* - var $registerTitle = new Ox.Element().html("Register:").appendTo(c); - var registerForm = c.$form = new Ox.Form({ - id: "registerForm", - items: $registerFormItems, - submit: function(data, callback) { - alert(JSON.stringify(data)); - app.api.register(data, function(result) { - alert(JSON.stringify(result)); - }); - - } -*/ -// c.html("login goes here"); + btnsWrapper.appendTo(c); return c; }, diff --git a/itf/static/js/itf/forms.js b/itf/static/js/itf/forms.js index 4613bb0..5237a3d 100644 --- a/itf/static/js/itf/forms.js +++ b/itf/static/js/itf/forms.js @@ -1,6 +1,6 @@ - +/* var $registerFormItems = (function() { var $username = new Ox.Input({ 'id': 'registerUsername', @@ -41,9 +41,14 @@ var $registerFormItems = (function() { return [$username, $password, $email] })(); +*/ +(function() { - var ui = { +//FIXME: creating global for ui. This doesn't sound healthy. + + + ui = { accountDialog: function(action) { var that = app.$ui.accountDialog = new Ox.Dialog($.extend({ height: 256, @@ -152,7 +157,7 @@ var $registerFormItems = (function() { }); } var items = { - 'login': ['username', 'password'], + 'login': ['usernameOrEmail', 'password'], 'register': ['newUsername', 'password', 'email'], 'reset': ['usernameOrEmail'], 'resetAndLogin': ['oldUsername', 'newPassword', 'code'] @@ -165,19 +170,19 @@ var $registerFormItems = (function() { items: $items, submit: function(data, callback) { if (action == 'login') { - pandora.api.login(data, function(result) { + app.api.login(data, function(result) { if (!result.data.errors) { app.$ui.accountDialog.close(); - login(result.data); + ITF.login(result.data); } else { callback([{id: 'password', message: 'Incorrect password'}]); } }); } else if (action == 'register') { - pandora.api.register(data, function(result) { + app.api.register(data, function(result) { if (!result.data.errors) { app.$ui.accountDialog.close(); - login(result.data); + ITF.login(result.data); ui.accountWelcomeDialog().open(); } else { callback([{id: 'password', message: result.data.errors.toString()}]); // fixme @@ -188,7 +193,7 @@ var $registerFormItems = (function() { key = usernameOrEmail[0].id; data = {}; data[key] = usernameOrEmail[1]; - pandora.api.requestToken(data, function(result) { + app.api.requestToken(data, function(result) { if (!result.data.errors) { app.$ui.accountDialog.options(ui.accountDialogOptions('resetAndLogin', result.data.username)); } else { @@ -196,10 +201,10 @@ var $registerFormItems = (function() { } }); } else if (action == 'resetAndLogin') { - pandora.api.resetPassword(data, function(result) { + app.api.resetPassword(data, function(result) { if (!result.data.errors) { app.$ui.accountDialog.close(); - login(result.data); + ITF.login(result.data); } else { callback([{id: 'code', message: 'Incorrect code'}]); } @@ -353,8 +358,8 @@ var $registerFormItems = (function() { title: 'Logout' }).bindEvent('click', function() { that.close(); - pandora.api.logout({}, function(result) { - logout(result.data); + app.api.logout({}, function(result) { + ITF.logout(result.data); }); }) ], @@ -433,7 +438,7 @@ function validateUser(key, existing) { var string = key == 'username' ? 'username' : 'e-mail address'; return function(value, callback) { var valid = key == 'username' ? !!value.length : Ox.isValidEmail(value); - valid ? pandora.api.findUser({ + valid ? app.api.findUser({ key: key, value: value, operator: '=' @@ -453,4 +458,4 @@ function validateUser(key, existing) { }; } - +})(); diff --git a/itf/static/js/itf/itf.js b/itf/static/js/itf/itf.js index 15781c9..7be1e68 100644 --- a/itf/static/js/itf/itf.js +++ b/itf/static/js/itf/itf.js @@ -1,6 +1,31 @@ var ITF = {}; +ITF.login = function(data) { + app.user = data.user; + var box = app.$ui.loginBox; + box.slideUp("slow", function() { + box.$loginBtn.hide(); + box.$registerBtn.hide(); + box.$info.html("logged in as " + data.user.username + " "); + box.$logoutBtn.show(); + box.slideDown(); + }); +} + +ITF.logout = function(data) { + app.user = data.user; + var box = app.$ui.loginBox; + box.slideUp("slow", function() { + box.$logoutBtn.hide(); + box.$info.html(''); + box.$loginBtn.show(); + box.$registerBtn.show(); + box.slideDown(); + }); +// alert("logged out"); +} + ITF.setSizes = function() { var mp = app.$ui.middlePanel; var winHeight = parseInt(mp.height()); @@ -18,6 +43,10 @@ ITF.setSizes = function() { mmp.size(2, panelWidth); mbp.size(0, panelWidth); mbp.size(2, panelWidth); + + //FIXME: FIX! + var listHeight = panelHeight - 22; + app.$ui.bestpracticesBox.$listContainer.css({'height': listHeight + "px"}); } diff --git a/itf/static/js/itf/widgets.js b/itf/static/js/itf/widgets.js index 88dffc1..ac8ed3b 100644 --- a/itf/static/js/itf/widgets.js +++ b/itf/static/js/itf/widgets.js @@ -51,13 +51,81 @@ Ox.ItfBox = function(options, self) { $additionalButtons.appendTo($buttons); } - var $maximizeBtn = new Ox.Button({ - id: options.id + "Maximize", + var $addBtn = new Ox.Button({ + id: options.id + "AddItem", style: 'symbol', title: 'add', type: 'image', - tooltip: 'Maximize' - }).appendTo($buttons); + tooltip: 'Add' + }) + .appendTo($buttons) + .bindEvent("click", function() { + app.$ui[options.boxId].$loading.start(); + app.api.getAddItemForm({ + 'module': that.options("module"), + 'model': that.options("model") + }, function(response) { + app.$ui[options.boxId].$loading.stop(); + var form = response.data.form; + var $content = new Ox.Element(); + var $items = []; + for (var i=0; i 0) { + var title = new Ox.Element("h4").html("Comments:").appendTo(that); + for (var i=0; i