From fa489ce37a6ce3f5f537b881a6d64134c9ce006b Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 20 Dec 2013 11:59:09 +0000 Subject: [PATCH] Upgrade Dajaxice to latest version from github (0.5.5) to make meeting agenda code work with CSRF, with url conf import fix and using json instead of deprecated simplejson from Django - add a simple view to serve dajaxice.core.js which we need because we are not using the staticfiles collection step (apparently Dajaxice hooks into that through a somewhat complicated hack) - Legacy-Id: 7028 --- dajaxice/__init__.py | 2 +- dajaxice/core/Dajaxice.py | 201 +++++-------- dajaxice/core/DajaxiceRequest.py | 228 --------------- dajaxice/core/__init__.py | 41 ++- dajaxice/decorators.py | 48 +++- dajaxice/exceptions.py | 49 +--- dajaxice/finders.py | 6 +- dajaxice/management/__init__.py | 0 dajaxice/management/commands/__init__.py | 0 .../commands/generate_static_dajaxice.py | 83 ------ dajaxice/models.py | 1 + dajaxice/templates/dajaxice/dajaxice.core.js | 133 +++++---- .../templates/dajaxice/dajaxice_core_loop.js | 16 -- .../dajaxice/dajaxice_function_loop.js | 4 +- .../dajaxice/dajaxice_js_import.html | 1 - .../dajaxice/dajaxice_module_loop.js | 5 +- .../templatetags/dajaxice_templatetags.py | 65 ++--- dajaxice/tests/__init__.py | 271 +++++++++--------- dajaxice/tests/ajax.py | 70 ++--- dajaxice/tests/submodules/__init__.py | 0 dajaxice/tests/submodules/ajax.py | 40 --- dajaxice/tests/urls.py | 40 +-- dajaxice/urls.py | 42 +-- dajaxice/utils.py | 46 +-- dajaxice/views.py | 103 ++++--- ietf/meeting/ajax.py | 6 + ietf/urls.py | 1 + 27 files changed, 504 insertions(+), 998 deletions(-) delete mode 100644 dajaxice/core/DajaxiceRequest.py delete mode 100644 dajaxice/management/__init__.py delete mode 100644 dajaxice/management/commands/__init__.py delete mode 100644 dajaxice/management/commands/generate_static_dajaxice.py delete mode 100644 dajaxice/templates/dajaxice/dajaxice_core_loop.js delete mode 100644 dajaxice/templates/dajaxice/dajaxice_js_import.html delete mode 100644 dajaxice/tests/submodules/__init__.py delete mode 100644 dajaxice/tests/submodules/ajax.py diff --git a/dajaxice/__init__.py b/dajaxice/__init__.py index 3d2aa1c0e..bf70dc73f 100644 --- a/dajaxice/__init__.py +++ b/dajaxice/__init__.py @@ -1 +1 @@ -VERSION = (0, 2, 0, 0, 'beta') +__version__ = (0, 5, 5, 'beta') diff --git a/dajaxice/core/Dajaxice.py b/dajaxice/core/Dajaxice.py index 7ccafbc2e..86aa6e0ed 100644 --- a/dajaxice/core/Dajaxice.py +++ b/dajaxice/core/Dajaxice.py @@ -1,156 +1,107 @@ -#import logging +import logging -from django.conf import settings +from django.utils.importlib import import_module -# Python 2.7 has an importlib with import_module. -# For older Pythons, Django's bundled copy provides it. -# For older Django's dajaxice reduced_import_module. -try: - from importlib import import_module -except: - try: - from django.utils.importlib import import_module - except: - from dajaxice.utils import simple_import_module as import_module - -#log = logging.getLogger('dajaxice.DajaxiceRequest') -import syslog -def warning(msg): - syslog.syslog(syslog.LOG_WANRNING, msg) -log = syslog -log.warning = warning +log = logging.getLogger('dajaxice') class DajaxiceFunction(object): + """ Basic representation of a dajaxice ajax function.""" - def __init__(self, name, path, doc=None): + def __init__(self, function, name, method): + self.function = function self.name = name - self.path = path - self.doc = doc + self.method = method - def get_callable_path(self): - return '%s.%s' % (self.path.replace('.ajax', ''), self.name) - - def __cmp__(self, other): - return (self.name == other.name and self.path == other.path) + def call(self, *args, **kwargs): + """ Call the function. """ + return self.function(*args, **kwargs) class DajaxiceModule(object): - def __init__(self, module): - self.functions = [] - self.sub_modules = [] - self.name = module[0] + """ Basic representation of a dajaxice module. """ - sub_module = module[1:] - if len(sub_module) != 0: - self.add_submodule(sub_module) + def __init__(self, name=None): + self.name = name + self.functions = {} + self.submodules = {} - def get_module(self, module): - """ - Recursively get_module util we found it. - """ - if len(module) == 0: - return self + def add(self, name, function): + """ Add this function at the ``name`` deep. If the submodule already + exists, recusively call the add method into the submodule. If not, + create the module and call the add method.""" - for dajaxice_module in self.sub_modules: - if dajaxice_module.name == module[0]: - return dajaxice_module.get_module(module[1:]) - return None - - def add_function(self, function): - self.functions.append(function) - - def has_sub_modules(self): - return len(self.sub_modules) > 0 - - def add_submodule(self, module): - """ - Recursively add_submodule, if it's not registered, create it. - """ - if len(module) == 0: - return + # If this is not the final function name (there are more modules) + # split the name again an register a new submodule. + if '.' in name: + module, extra = name.split('.', 1) + if module not in self.submodules: + self.submodules[module] = DajaxiceModule(module) + self.submodules[module].add(extra, function) else: - sub_module = self.exist_submodule(module[0]) - - if type(sub_module) == int: - self.sub_modules[sub_module].add_submodule(module[1:]) - else: - self.sub_modules.append(DajaxiceModule(module)) - - def exist_submodule(self, name): - """ - Check if submodule name was already registered. - """ - for module in self.sub_modules: - if module.name == name: - return self.sub_modules.index(module) - return False + self.functions[name] = function class Dajaxice(object): + def __init__(self): - self._registry = [] - self._callable = [] + self._registry = {} + self._modules = None - for function in getattr(settings, 'DAJAXICE_FUNCTIONS', ()): - function = function.rsplit('.', 1) - self.register_function(function[0], function[1]) - - def register(self, function): - self.register_function(function.__module__, function.__name__, function.__doc__) - - def register_function(self, module, name, doc=None): + def register(self, function, name=None, method='POST'): """ - Register function at 'module' depth - """ - #Create the dajaxice function. - function = DajaxiceFunction(name=name, path=module, doc=doc) + Register this function as a dajaxice function. - #Check for already registered functions. - full_path = '%s.%s' % (module, name) - if full_path in self._callable: - log.warning('%s already registered as dajaxice function.' % full_path) + If no name is provided, the module and the function name will be used. + The final (customized or not) must be unique. """ + + method = self.clean_method(method) + + # Generate a default name + if not name: + module = ''.join(str(function.__module__).rsplit('.ajax', 1)) + name = '.'.join((module, function.__name__)) + + if ':' in name: + log.error('Ivalid function name %s.' % name) return - self._callable.append(full_path) + # Check for already registered functions + if name in self._registry: + log.error('%s was already registered.' % name) + return - #Dajaxice path without ajax. - module_without_ajax = module.replace('.ajax', '').split('.') + # Create the dajaxice function. + function = DajaxiceFunction(function=function, + name=name, + method=method) - #Register module if necessary. - exist_module = self._exist_module(module_without_ajax[0]) + # Register this new ajax function + self._registry[name] = function - if type(exist_module) == int: - self._registry[exist_module].add_submodule(module_without_ajax[1:]) - else: - self._registry.append(DajaxiceModule(module_without_ajax)) + def is_callable(self, name, method): + """ Return if the function callable or not. """ + return name in self._registry and self._registry[name].method == method - #Register Function - module = self.get_module(module_without_ajax) - if module: - module.add_function(function) + def clean_method(self, method): + """ Clean the http method. """ + method = method.upper() + if method not in ['GET', 'POST']: + method = 'POST' + return method - def get_module(self, module): - """ - Recursively get module from registry - """ - for dajaxice_module in self._registry: - if dajaxice_module.name == module[0]: - return dajaxice_module.get_module(module[1:]) - return None - - def is_callable(self, name): - return name in self._callable - - def _exist_module(self, module_name): - for module in self._registry: - if module.name == module_name: - return self._registry.index(module) - return False - - def get_functions(self): - return self._registry + def get(self, name): + """ Return the dajaxice function.""" + return self._registry[name] + @property + def modules(self): + """ Return an easy to loop module hierarchy with all the functions.""" + if not self._modules: + self._modules = DajaxiceModule() + for name, function in self._registry.items(): + self._modules.add(name, function) + return self._modules LOADING_DAJAXICE = False @@ -158,8 +109,8 @@ LOADING_DAJAXICE = False def dajaxice_autodiscover(): """ Auto-discover INSTALLED_APPS ajax.py modules and fail silently when - not present. - NOTE: dajaxice_autodiscover was inspired/copied from django.contrib.admin autodiscover + not present. NOTE: dajaxice_autodiscover was inspired/copied from + django.contrib.admin autodiscover """ global LOADING_DAJAXICE if LOADING_DAJAXICE: diff --git a/dajaxice/core/DajaxiceRequest.py b/dajaxice/core/DajaxiceRequest.py deleted file mode 100644 index ee6802628..000000000 --- a/dajaxice/core/DajaxiceRequest.py +++ /dev/null @@ -1,228 +0,0 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - -import os -import sys -#import logging -import traceback -import json - -from django.conf import settings -from django.http import HttpResponse - -from dajaxice.core import dajaxice_functions -from dajaxice.exceptions import FunctionNotCallableError, DajaxiceImportError - -#log = logging.getLogger('dajaxice.DajaxiceRequest') -import syslog -def debug(msg): - syslog.syslog(syslog.LOG_DEBUG, msg) -def info(msg): - syslog.syslog(syslog.LOG_INFO, msg) -def warning(msg): - syslog.syslog(syslog.LOG_WANRNING, msg) -def error(msg): - syslog.syslog(syslog.LOG_ERR, msg) -log = syslog -log.debug = debug -log.info = info -log.warning = warning -log.error = error - -# Python 2.7 has an importlib with import_module. -# For older Pythons, Django's bundled copy provides it. -# For older Django's dajaxice reduced_import_module. -try: - from importlib import import_module -except: - try: - from django.utils.importlib import import_module - except: - from dajaxice.utils import simple_import_module as import_module - - -def safe_dict(d): - """ - Recursively clone json structure with UTF-8 dictionary keys - http://www.gossamer-threads.com/lists/python/bugs/684379 - """ - if isinstance(d, dict): - return dict([(k.encode('utf-8'), safe_dict(v)) for k, v in d.iteritems()]) - elif isinstance(d, list): - return [safe_dict(x) for x in d] - else: - return d - - -class DajaxiceRequest(object): - - def __init__(self, request, call): - call = call.rsplit('.', 1) - self.app_name = call[0] - self.method = call[1] - self.request = request - - self.project_name = os.environ['DJANGO_SETTINGS_MODULE'].split('.')[0] - self.module = "%s.ajax" % self.app_name - self.full_name = "%s.%s" % (self.module, self.method) - - @staticmethod - def get_js_functions(): - return dajaxice_functions.get_functions() - - @staticmethod - def get_media_prefix(): - return getattr(settings, 'DAJAXICE_MEDIA_PREFIX', "dajaxice") - - @staticmethod - def get_functions(): - return getattr(settings, 'DAJAXICE_FUNCTIONS', ()) - - @staticmethod - def get_debug(): - return getattr(settings, 'DAJAXICE_DEBUG', True) - - @staticmethod - def get_notify_exceptions(): - return getattr(settings, 'DAJAXICE_NOTIFY_EXCEPTIONS', False) - - @staticmethod - def get_cache_control(): - if settings.DEBUG: - return 0 - return getattr(settings, 'DAJAXICE_CACHE_CONTROL', 5 * 24 * 60 * 60) - - @staticmethod - def get_xmlhttprequest_js_import(): - return getattr(settings, 'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT', True) - - @staticmethod - def get_json2_js_import(): - return getattr(settings, 'DAJAXICE_JSON2_JS_IMPORT', True) - - @staticmethod - def get_exception_message(): - return getattr(settings, 'DAJAXICE_EXCEPTION', u'DAJAXICE_EXCEPTION') - - @staticmethod - def get_js_docstrings(): - return getattr(settings, 'DAJAXICE_JS_DOCSTRINGS', False) - - def _is_callable(self): - """ - Return if the request function was registered. - """ - return dajaxice_functions.is_callable(self.full_name) - - def _get_ajax_function(self): - """ - Return a callable ajax function. - This function should be imported according the Django version. - """ - return self._modern_get_ajax_function() - - def _modern_get_ajax_function(self): - """ - Return a callable ajax function. - This function uses django.utils.importlib - """ - self.module_import_name = "%s.%s" % (self.project_name, self.module) - try: - return self._modern_import() - except: - self.module_import_name = self.module - return self._modern_import() - - def _modern_import(self): - try: - mod = import_module(self.module_import_name) - return mod.__getattribute__(self.method) - except: - raise DajaxiceImportError() - - def process(self): - """ - Process the dajax request calling the apropiate method. - """ - if self._is_callable(): - log.debug('Function %s is callable' % self.full_name) - - argv = self.request.POST.get('argv') - if argv != 'undefined': - try: - argv = json.loads(self.request.POST.get('argv')) - argv = safe_dict(argv) - except Exception, e: - log.error('argv exception %s' % e) - argv = {} - else: - argv = {} - - log.debug('argv %s' % argv) - - try: - thefunction = self._get_ajax_function() - response = '%s' % thefunction(self.request, **argv) - - except Exception, e: - trace = '\n'.join(traceback.format_exception(*sys.exc_info())) - log.error(trace) - response = '%s' % DajaxiceRequest.get_exception_message() - - if DajaxiceRequest.get_notify_exceptions(): - self.notify_exception(self.request, sys.exc_info()) - - log.info('response: %s' % response) - return HttpResponse(response, mimetype="application/x-json") - - else: - log.debug('Function %s is not callable' % self.full_name) - raise FunctionNotCallableError(name=self.full_name) - - def notify_exception(self, request, exc_info): - """ - Send Exception traceback to ADMINS - Similar to BaseHandler.handle_uncaught_exception - """ - from django.conf import settings - from django.core.mail import mail_admins - - subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path) - try: - request_repr = repr(request) - except: - request_repr = "Request repr() unavailable" - - trace = '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info()))) - message = "%s\n\n%s" % (trace, request_repr) - mail_admins(subject, message, fail_silently=True) diff --git a/dajaxice/core/__init__.py b/dajaxice/core/__init__.py index e7ea9c1e3..d87543fba 100644 --- a/dajaxice/core/__init__.py +++ b/dajaxice/core/__init__.py @@ -1,5 +1,38 @@ -from Dajaxice import Dajaxice -dajaxice_functions = Dajaxice() +from django.conf import settings -from DajaxiceRequest import DajaxiceRequest -from Dajaxice import dajaxice_autodiscover +from Dajaxice import Dajaxice, dajaxice_autodiscover + + +class DajaxiceConfig(object): + """ Provide an easy to use way to read the dajaxice configuration and + return the default values if no configuration is present.""" + + default_config = {'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT': True, + 'DAJAXICE_JSON2_JS_IMPORT': True, + 'DAJAXICE_EXCEPTION': 'DAJAXICE_EXCEPTION', + 'DAJAXICE_MEDIA_PREFIX': 'dajaxice'} + + def __getattr__(self, name): + """ Return the customized value for a setting (if it exists) or the + default value if not. """ + + if name in self.default_config: + if hasattr(settings, name): + return getattr(settings, name) + return self.default_config.get(name) + return None + + @property + def dajaxice_url(self): + return r'^%s/' % self.DAJAXICE_MEDIA_PREFIX + + @property + def django_settings(self): + return settings + + @property + def modules(self): + return dajaxice_functions.modules + +dajaxice_functions = Dajaxice() +dajaxice_config = DajaxiceConfig() diff --git a/dajaxice/decorators.py b/dajaxice/decorators.py index d3415449e..44df89ead 100644 --- a/dajaxice/decorators.py +++ b/dajaxice/decorators.py @@ -1,10 +1,48 @@ +import functools + from dajaxice.core import dajaxice_functions -def dajaxice_register(original_function): - """ - Register the original funcion and returns it +def dajaxice_register(*dargs, **dkwargs): + """ Register some function as a dajaxice function + + For legacy purposes, if only a function is passed register it a simple + single ajax function using POST, i.e: + + @dajaxice_register + def ajax_function(request): + ... + + After 0.5, dajaxice allow to customize the http method and the final name + of the registered function. This decorator covers both the legacy and + the new functionality, i.e: + + @dajaxice_register(method='GET') + def ajax_function(request): + ... + + @dajaxice_register(method='GET', name='my.custom.name') + def ajax_function(request): + ... + + You can also register the same function to use a different http method + and/or use a different name. + + @dajaxice_register(method='GET', name='users.get') + @dajaxice_register(method='POST', name='users.update') + def ajax_function(request): + ... """ - dajaxice_functions.register(original_function) - return original_function + if len(dargs) and not dkwargs: + function = dargs[0] + dajaxice_functions.register(function) + return function + + def decorator(function): + @functools.wraps(function) + def wrapper(request, *args, **kwargs): + return function(request, *args, **kwargs) + dajaxice_functions.register(function, *dargs, **dkwargs) + return wrapper + return decorator diff --git a/dajaxice/exceptions.py b/dajaxice/exceptions.py index 9ce08eee5..139ba6282 100644 --- a/dajaxice/exceptions.py +++ b/dajaxice/exceptions.py @@ -1,41 +1,10 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - - -class FunctionNotCallableError(Exception): - def __init__(self, name): - self.name = name - - -class DajaxiceImportError(Exception): +class DajaxiceError(Exception): + pass + + +class FunctionNotCallableError(DajaxiceError): + pass + + +class DajaxiceImportError(DajaxiceError): pass diff --git a/dajaxice/finders.py b/dajaxice/finders.py index f07416366..5178b22e5 100644 --- a/dajaxice/finders.py +++ b/dajaxice/finders.py @@ -44,7 +44,7 @@ class VirtualStorage(finders.FileSystemStorage): for f in self.files: if f.startswith(path): f = f.replace(path, '', 1) - if os.sep in f: + if os.sep in f: folders.append(f.split(os.sep, 1)[0]) else: files.append(f) @@ -60,7 +60,7 @@ class VirtualStorage(finders.FileSystemStorage): class DajaxiceStorage(VirtualStorage): - files = {'dajaxice/dajaxice.core.js': 'dajaxice_core_js'} + files = {os.path.join('dajaxice', 'dajaxice.core.js'): 'dajaxice_core_js'} def dajaxice_core_js(self): from dajaxice.core import dajaxice_autodiscover, dajaxice_config @@ -68,7 +68,7 @@ class DajaxiceStorage(VirtualStorage): dajaxice_autodiscover() c = Context({'dajaxice_config': dajaxice_config}) - return get_template('dajaxice/dajaxice.core.js').render(c) + return get_template(os.path.join('dajaxice', 'dajaxice.core.js')).render(c) class DajaxiceFinder(finders.BaseStorageFinder): diff --git a/dajaxice/management/__init__.py b/dajaxice/management/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/dajaxice/management/commands/__init__.py b/dajaxice/management/commands/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/dajaxice/management/commands/generate_static_dajaxice.py b/dajaxice/management/commands/generate_static_dajaxice.py deleted file mode 100644 index 0edc92444..000000000 --- a/dajaxice/management/commands/generate_static_dajaxice.py +++ /dev/null @@ -1,83 +0,0 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - -import httplib -import urllib - -from django.core.management.base import BaseCommand -from django.template.loader import render_to_string -from optparse import make_option - -from dajaxice.core import DajaxiceRequest -from dajaxice.core import dajaxice_autodiscover -dajaxice_autodiscover() - - -class Command(BaseCommand): - help = "Generate dajaxice.core.js file to import it as static file" - args = "[--compile]" - option_list = BaseCommand.option_list + ( - make_option('--compile', default='no', dest='compile', help='Compile output using Google closure-compiler'), - ) - - requires_model_validation = False - - def handle(self, *app_labels, **options): - compile_output = options.get('compile', 'yes') - data = {'dajaxice_js_functions': DajaxiceRequest.get_js_functions(), - 'DAJAXICE_URL_PREFIX': DajaxiceRequest.get_media_prefix(), - 'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT': DajaxiceRequest.get_xmlhttprequest_js_import(), - 'DAJAXICE_JSON2_JS_IMPORT': DajaxiceRequest.get_json2_js_import(), - 'DAJAXICE_EXCEPTION': DajaxiceRequest.get_exception_message()} - - js = render_to_string('dajaxice/dajaxice.core.js', data) - if compile_output.lower() == "closure": - print self.complie_js_with_closure(js) - else: - print js - - def complie_js_with_closure(self, js): - params = urllib.urlencode([ - ('js_code', js), - ('compilation_level', 'ADVANCED_OPTIMIZATIONS'), - ('output_format', 'text'), - ('output_info', 'compiled_code'), - ]) - # Always use the following value for the Content-type header. - headers = {"Content-type": "application/x-www-form-urlencoded"} - conn = httplib.HTTPConnection('closure-compiler.appspot.com') - conn.request('POST', '/compile', params, headers) - response = conn.getresponse() - data = response.read() - conn.close() - return data diff --git a/dajaxice/models.py b/dajaxice/models.py index e69de29bb..36e89fccb 100644 --- a/dajaxice/models.py +++ b/dajaxice/models.py @@ -0,0 +1 @@ +# Don't delete me diff --git a/dajaxice/templates/dajaxice/dajaxice.core.js b/dajaxice/templates/dajaxice/dajaxice.core.js index d4b92648d..a6ceed561 100644 --- a/dajaxice/templates/dajaxice/dajaxice.core.js +++ b/dajaxice/templates/dajaxice/dajaxice.core.js @@ -1,8 +1,14 @@ +{% load url from future %} var Dajaxice = { - {% for module in dajaxice_js_functions %} - {% include "dajaxice/dajaxice_core_loop.js" %} - {% endfor %}{% ifnotequal dajaxice_js_functions|length 0 %},{% endifnotequal %} - + + {% with module=dajaxice_config.modules top='top' %} + {% include "dajaxice/dajaxice_function_loop.js" %} + {% endwith %} + + {% for name, module in dajaxice_config.modules.submodules.items %} + {% include "dajaxice/dajaxice_module_loop.js" %}, + {% endfor %} + get_cookie: function(name) { var cookieValue = null; @@ -19,95 +25,106 @@ var Dajaxice = { } return cookieValue; }, - - call: function(dajaxice_function, dajaxice_callback, argv, exception_callback) + + call: function(dajaxice_function, method, dajaxice_callback, argv, custom_settings) { - var send_data = []; - var is_callback_a_function = (typeof(dajaxice_callback) == 'function'); - if(!is_callback_a_function){ - alert("dajaxice_callback should be a function since dajaxice 0.2") + var custom_settings = custom_settings || {}, + error_callback = Dajaxice.get_setting('default_exception_callback'); + + if('error_callback' in custom_settings && typeof(custom_settings['error_callback']) == 'function'){ + error_callback = custom_settings['error_callback']; } - - if(exception_callback==undefined || typeof(dajaxice_callback) != 'function'){ - exception_callback = this.get_setting('default_exception_callback'); + + var send_data = 'argv='+encodeURIComponent(JSON.stringify(argv)), + oXMLHttpRequest = new XMLHttpRequest, + endpoint = '{% url 'dajaxice-endpoint' %}'+dajaxice_function+'/'; + + if(method == 'GET'){ + endpoint = endpoint + '?' + send_data; } - - send_data.push('argv='+encodeURIComponent(JSON.stringify(argv))); - send_data = send_data.join('&'); - var oXMLHttpRequest = new XMLHttpRequest; - oXMLHttpRequest.open('POST', '/{{DAJAXICE_URL_PREFIX}}/'+dajaxice_function+'/'); + oXMLHttpRequest.open(method, endpoint); + oXMLHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); oXMLHttpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - oXMLHttpRequest.setRequestHeader("X-CSRFToken",Dajaxice.get_cookie('csrftoken')); + oXMLHttpRequest.setRequestHeader("X-CSRFToken", Dajaxice.get_cookie('{{ dajaxice_config.django_settings.CSRF_COOKIE_NAME }}')); oXMLHttpRequest.onreadystatechange = function() { if (this.readyState == XMLHttpRequest.DONE) { - if(this.responseText == Dajaxice.EXCEPTION){ - exception_callback(); + if(this.responseText == Dajaxice.EXCEPTION || !(this.status in Dajaxice.valid_http_responses())){ + error_callback(); } else{ - try{ - dajaxice_callback(JSON.parse(this.responseText)); + var response; + try { + response = JSON.parse(this.responseText); } - catch(exception){ - dajaxice_callback(this.responseText); + catch (exception) { + response = this.responseText; } + dajaxice_callback(response); } } } - oXMLHttpRequest.send(send_data); + if(method == 'POST'){ + oXMLHttpRequest.send(send_data); + } + else{ + oXMLHttpRequest.send(); + } + return oXMLHttpRequest; }, - + setup: function(settings) { this.settings = settings; }, - + get_setting: function(key){ if(this.settings == undefined || this.settings[key] == undefined){ - return this.default_settings[key]; + return Dajaxice.default_settings[key]; } return this.settings[key]; }, - - default_exception_callback: function(data){ - alert('Something goes wrong'); - } -}; -Dajaxice.EXCEPTION = '{{ DAJAXICE_EXCEPTION }}'; -Dajaxice.default_settings = {'default_exception_callback': Dajaxice.default_exception_callback} + valid_http_responses: function(){ + return {200: null, 301: null, 302: null, 304: null} + }, + + EXCEPTION: '{{ dajaxice_config.DAJAXICE_EXCEPTION }}', + default_settings: {'default_exception_callback': function(){ console.log('Dajaxice: Something went wrong.')}} +}; window['Dajaxice'] = Dajaxice; {% comment %} /* XMLHttpRequest.js Compiled with Google Closure - + XMLHttpRequest.js Copyright (C) 2008 Sergey Ilinsky (http://www.ilinsky.com) - + This work is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This work is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ {% endcomment %} -{% if DAJAXICE_XMLHTTPREQUEST_JS_IMPORT %} -(function(){function b(){this._object=i?new i:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function k(a){b.onreadystatechange&&b.onreadystatechange.apply(a);a.dispatchEvent({type:"readystatechange",bubbles:false,cancelable:false,timeStamp:new Date+0})}function p(a){var c=a.responseXML,d=a.responseText;if(h&&d&&c&&!c.documentElement&&a.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)){c=new window.ActiveXObject("Microsoft.XMLDOM");c.async=false;c.validateOnParse=false; -c.loadXML(d)}if(c)if(h&&c.parseError!=0||!c.documentElement||c.documentElement&&c.documentElement.tagName=="parsererror")return null;return c}function o(a){try{a.responseText=a._object.responseText}catch(c){}try{a.responseXML=p(a._object)}catch(d){}try{a.status=a._object.status}catch(g){}try{a.statusText=a._object.statusText}catch(e){}}function l(a){a._object.onreadystatechange=new window.Function}var i=window.XMLHttpRequest,j=!!window.controllers,h=window.document.all&&!window.opera;if(j&&i.wrapped)b.wrapped= -i.wrapped;b.UNSENT=0;b.OPENED=1;b.HEADERS_RECEIVED=2;b.LOADING=3;b.DONE=4;b.prototype.readyState=b.UNSENT;b.prototype.responseText="";b.prototype.responseXML=null;b.prototype.status=0;b.prototype.statusText="";b.prototype.onreadystatechange=null;b.onreadystatechange=null;b.onopen=null;b.onsend=null;b.onabort=null;b.prototype.open=function(a,c,d,g,e){delete this._headers;if(arguments.length<3)d=true;this._async=d;var f=this,m=this.readyState,n;if(h&&d){n=function(){if(m!=b.DONE){l(f);f.abort()}};window.attachEvent("onunload", -n)}b.onopen&&b.onopen.apply(this,arguments);if(arguments.length>4)this._object.open(a,c,d,g,e);else arguments.length>3?this._object.open(a,c,d,g):this._object.open(a,c,d);if(!j&&!h){this.readyState=b.OPENED;k(this)}this._object.onreadystatechange=function(){if(!(j&&!d)){f.readyState=f._object.readyState;o(f);if(f._aborted)f.readyState=b.UNSENT;else{if(f.readyState==b.DONE){l(f);h&&d&&window.detachEvent("onunload",n)}m!=f.readyState&&k(f);m=f.readyState}}}};b.prototype.send=function(a){b.onsend&&b.onsend.apply(this, -arguments);if(a&&a.nodeType){a=window.XMLSerializer?(new window.XMLSerializer).serializeToString(a):a.xml;this._headers["Content-Type"]||this._object.setRequestHeader("Content-Type","application/xml")}this._object.send(a);if(j&&!this._async){this.readyState=b.OPENED;for(o(this);this.readyStateb.UNSENT)this._aborted=true;this._object.abort();l(this)}; -b.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};b.prototype.getResponseHeader=function(a){return this._object.getResponseHeader(a)};b.prototype.setRequestHeader=function(a,c){if(!this._headers)this._headers={};this._headers[a]=c;return this._object.setRequestHeader(a,c)};b.prototype.addEventListener=function(a,c,d){for(var g=0,e;e=this._listeners[g];g++)if(e[0]==a&&e[1]==c&&e[2]==d)return;this._listeners.push([a,c,d])};b.prototype.removeEventListener=function(a, -c,d){for(var g=0,e;e=this._listeners[g];g++)if(e[0]==a&&e[1]==c&&e[2]==d)break;e&&this._listeners.splice(g,1)};b.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){},initEvent:function(){}};if(a.type=="readystatechange"&&this.onreadystatechange)(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var c=0,d;d= -this._listeners[c];c++)if(d[0]==a.type&&!d[2])(d[1].handleEvent||d[1]).apply(this,[a])};b.prototype.toString=function(){return"[object XMLHttpRequest]"};b.toString=function(){return"[XMLHttpRequest]"};if(!window.Function.prototype.apply)window.Function.prototype.apply=function(a,c){c||(c=[]);a.__func=this;a.__func(c[0],c[1],c[2],c[3],c[4]);delete a.__func};window.XMLHttpRequest=b})(); +{% if dajaxice_config.DAJAXICE_XMLHTTPREQUEST_JS_IMPORT %} +(function(){function n(){this._object=h&&!p?new h:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function a(){return new n}function j(b){a.onreadystatechange&&a.onreadystatechange.apply(b);b.dispatchEvent({type:"readystatechange",bubbles:!1,cancelable:!1,timeStamp:new Date+0})}function o(b){try{b.responseText=b._object.responseText}catch(a){}try{var d;var g=b._object,c=g.responseXML,f=g.responseText;i&&(f&&c&&!c.documentElement&&g.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/))&& +(c=new window.ActiveXObject("Microsoft.XMLDOM"),c.async=!1,c.validateOnParse=!1,c.loadXML(f));d=c&&(i&&0!==c.parseError||!c.documentElement||c.documentElement&&"parsererror"==c.documentElement.tagName)?null:c;b.responseXML=d}catch(h){}try{b.status=b._object.status}catch(k){}try{b.statusText=b._object.statusText}catch(j){}}function l(b){b._object.onreadystatechange=new window.Function}var h=window.XMLHttpRequest,m=!!window.controllers,i=window.document.all&&!window.opera,p=i&&window.navigator.userAgent.match(/MSIE 7.0/); +a.prototype=n.prototype;m&&h.wrapped&&(a.wrapped=h.wrapped);a.UNSENT=0;a.OPENED=1;a.HEADERS_RECEIVED=2;a.LOADING=3;a.DONE=4;a.prototype.readyState=a.UNSENT;a.prototype.responseText="";a.prototype.responseXML=null;a.prototype.status=0;a.prototype.statusText="";a.prototype.priority="NORMAL";a.prototype.onreadystatechange=null;a.onreadystatechange=null;a.onopen=null;a.onsend=null;a.onabort=null;a.prototype.open=function(b,e,d,g,c){delete this._headers;arguments.length<3&&(d=true);this._async=d;var f= +this,h=this.readyState,k=null;if(i&&d){k=function(){if(h!=a.DONE){l(f);f.abort()}};window.attachEvent("onunload",k)}a.onopen&&a.onopen.apply(this,arguments);arguments.length>4?this._object.open(b,e,d,g,c):arguments.length>3?this._object.open(b,e,d,g):this._object.open(b,e,d);this.readyState=a.OPENED;j(this);this._object.onreadystatechange=function(){if(!m||d){f.readyState=f._object.readyState;o(f);if(f._aborted)f.readyState=a.UNSENT;else if(f.readyState==a.DONE){delete f._data;l(f);i&&d&&window.detachEvent("onunload", +k);h!=f.readyState&&j(f);h=f.readyState}}}};a.prototype.send=function(b){a.onsend&&a.onsend.apply(this,arguments);arguments.length||(b=null);if(b&&b.nodeType){b=window.XMLSerializer?(new window.XMLSerializer).serializeToString(b):b.xml;this._headers["Content-Type"]||this._object.setRequestHeader("Content-Type","application/xml")}this._data=b;a:{this._object.send(this._data);if(m&&!this._async){this.readyState=a.OPENED;for(o(this);this.readyStatea.UNSENT)this._aborted=true;this._object.abort();l(this);this.readyState=a.UNSENT;delete this._data};a.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};a.prototype.getResponseHeader=function(b){return this._object.getResponseHeader(b)};a.prototype.setRequestHeader=function(b,a){if(!this._headers)this._headers={};this._headers[b]=a;return this._object.setRequestHeader(b, +a)};a.prototype.addEventListener=function(a,e,d){for(var g=0,c;c=this._listeners[g];g++)if(c[0]==a&&c[1]==e&&c[2]==d)return;this._listeners.push([a,e,d])};a.prototype.removeEventListener=function(a,e,d){for(var g=0,c;c=this._listeners[g];g++)if(c[0]==a&&c[1]==e&&c[2]==d)break;c&&this._listeners.splice(g,1)};a.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){}, +initEvent:function(){}};a.type=="readystatechange"&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var e=0,d;d=this._listeners[e];e++)d[0]==a.type&&!d[2]&&(d[1].handleEvent||d[1]).apply(this,[a])};a.prototype.toString=function(){return"[object XMLHttpRequest]"};a.toString=function(){return"[XMLHttpRequest]"};window.Function.prototype.apply||(window.Function.prototype.apply=function(a,e){e||(e=[]);a.__func=this;a.__func(e[0],e[1],e[2],e[3], +e[4]);delete a.__func});window.XMLHttpRequest=a})(); {% endif %} {% comment %} @@ -117,12 +134,12 @@ this._listeners[c];c++)if(d[0]==a.type&&!d[2])(d[1].handleEvent||d[1]).apply(thi I hope that in the future this code won't be neccessary, but today all browsers doesn't supports JSON.stringify(). */ {% endcomment %} -{% if DAJAXICE_JSON2_JS_IMPORT %} -if(!this.JSON)this.JSON={}; -(function(){function k(a){return a<10?"0"+a:a}function n(a){o.lastIndex=0;return o.test(a)?'"'+a.replace(o,function(c){var d=q[c];return typeof d==="string"?d:"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function l(a,c){var d,f,i=g,e,b=c[a];if(b&&typeof b==="object"&&typeof b.toJSON==="function")b=b.toJSON(a);if(typeof j==="function")b=j.call(c,a,b);switch(typeof b){case "string":return n(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null"; -g+=m;e=[];if(Object.prototype.toString.apply(b)==="[object Array]"){f=b.length;for(a=0;aa?"0"+a:a}function o(a){p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function(a){var c=r[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function m(a,j){var c,d,h,n,g=e,f,b=j[a];b&&("object"===typeof b&&"function"===typeof b.toJSON)&&(b=b.toJSON(a));"function"===typeof i&&(b=i.call(j,a,b));switch(typeof b){case "string":return o(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null"; +e+=l;f=[];if("[object Array]"===Object.prototype.toString.apply(b)){n=b.length;for(c=0;c \ No newline at end of file diff --git a/dajaxice/templates/dajaxice/dajaxice_module_loop.js b/dajaxice/templates/dajaxice/dajaxice_module_loop.js index 3abadbc8c..2604f1c8a 100644 --- a/dajaxice/templates/dajaxice/dajaxice_module_loop.js +++ b/dajaxice/templates/dajaxice/dajaxice_module_loop.js @@ -1,8 +1,11 @@ {{ name }}: { {% include "dajaxice/dajaxice_function_loop.js" %} + {% with parent_foorloop=forloop %} {% for name, sub_module in module.submodules.items %} {% with filename="dajaxice/dajaxice_module_loop.js" module=sub_module %} {% include filename %} {% endwith %} + {% if not forloop.last %},{% endif %} {% endfor %} - }{% if not forloop.last %},{% endif %} + } + {% endwith %} diff --git a/dajaxice/templatetags/dajaxice_templatetags.py b/dajaxice/templatetags/dajaxice_templatetags.py index 9c622d142..587305b2b 100644 --- a/dajaxice/templatetags/dajaxice_templatetags.py +++ b/dajaxice/templatetags/dajaxice_templatetags.py @@ -1,42 +1,35 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- +import logging from django import template -from dajaxice.core import DajaxiceRequest +from django.middleware.csrf import get_token +from django.conf import settings +from django.core.files.storage import get_storage_class + +staticfiles_storage = get_storage_class(settings.STATICFILES_STORAGE)() register = template.Library() +log = logging.getLogger('dajaxice') -@register.inclusion_tag('dajaxice/dajaxice_js_import.html', takes_context=True) -def dajaxice_js_import(context): - return {'DAJAXICE_MEDIA_PREFIX': DajaxiceRequest.get_media_prefix()} + +@register.simple_tag(takes_context=True) +def dajaxice_js_import(context, csrf=True): + """ Return the js script tag for the dajaxice.core.js file + If the csrf argument is present and it's ``nocsrf`` dajaxice will not + try to mark the request as if it need the csrf token. By default use + the dajaxice_js_import template tag will make django set the csrftoken + cookie on the current request.""" + + csrf = csrf != 'nocsrf' + request = context.get('request') + + if request and csrf: + get_token(request) + elif csrf: + log.warning("The 'request' object must be accesible within the " + "context. You must add 'django.contrib.messages.context" + "_processors.request' to your TEMPLATE_CONTEXT_PROCESSORS " + "and render your views using a RequestContext.") + + url = staticfiles_storage.url('dajaxice/dajaxice.core.js') + return '' % url diff --git a/dajaxice/tests/__init__.py b/dajaxice/tests/__init__.py index 74723db14..fb918e59e 100644 --- a/dajaxice/tests/__init__.py +++ b/dajaxice/tests/__init__.py @@ -1,46 +1,114 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - -import os -import unittest - from django.test import TestCase + from django.conf import settings -from dajaxice.exceptions import FunctionNotCallableError, DajaxiceImportError -from dajaxice.core import DajaxiceRequest -from dajaxice.core.Dajaxice import Dajaxice, DajaxiceModule, DajaxiceFunction -from dajaxice.core import dajaxice_functions +from dajaxice.core import DajaxiceConfig +from dajaxice.core.Dajaxice import DajaxiceModule, DajaxiceFunction, Dajaxice +from dajaxice.exceptions import FunctionNotCallableError + + +class DajaxiceModuleTest(TestCase): + + def setUp(self): + self.module = DajaxiceModule() + + def test_constructor(self): + self.assertEqual(self.module.functions, {}) + self.assertEqual(self.module.submodules, {}) + + def test_add_function(self): + function = lambda x: x + + self.module.add('test', function) + self.assertEqual(self.module.functions, {'test': function}) + self.assertEqual(self.module.submodules, {}) + + self.module.add('foo.bar', function) + self.assertEqual(self.module.functions, {'test': function}) + self.assertEqual(self.module.submodules.keys(), ['foo']) + self.assertEqual(self.module.submodules['foo'].functions, {'bar': function}) + + +class DajaxiceFunctionTest(TestCase): + + def test_constructor(self): + + class CalledEception(Exception): + pass + + def callback(): + raise CalledEception + + function = DajaxiceFunction(callback, 'foo', 'POST') + + self.assertEqual(function.function, callback) + self.assertEqual(function.name, 'foo') + self.assertEqual(function.method, 'POST') + self.assertRaises(CalledEception, function.call) + + +class DajaxiceTest(TestCase): + + def setUp(self): + self.dajaxice = Dajaxice() + self.function = lambda x: x + + def test_constructor(self): + self.assertEqual(self.dajaxice._registry, {}) + + def test_register(self): + self.dajaxice.register(self.function, 'foo') + self.assertTrue('foo' in self.dajaxice._registry) + self.assertEqual(type(self.dajaxice._registry['foo']), DajaxiceFunction) + + def bar_function(): + return + + self.dajaxice.register(bar_function) + self.assertTrue('dajaxice.tests.bar_function' in self.dajaxice._registry) + self.assertEqual(type(self.dajaxice._registry['dajaxice.tests.bar_function']), DajaxiceFunction) + + def test__is_callable(self): + self.dajaxice.register(self.function, 'foo') + self.dajaxice.register(self.function, 'bar', method='GET') + + self.assertTrue(self.dajaxice.is_callable('foo', 'POST')) + self.assertTrue(self.dajaxice.is_callable('bar', 'GET')) + self.assertFalse(self.dajaxice.is_callable('foo', 'GET')) + self.assertFalse(self.dajaxice.is_callable('bar', 'POST')) + self.assertFalse(self.dajaxice.is_callable('test', 'POST')) + self.assertFalse(self.dajaxice.is_callable('test', 'GET')) + + def test_clean_method(self): + self.assertEqual(self.dajaxice.clean_method('post'), 'POST') + self.assertEqual(self.dajaxice.clean_method('get'), 'GET') + self.assertEqual(self.dajaxice.clean_method('POST'), 'POST') + self.assertEqual(self.dajaxice.clean_method('GET'), 'GET') + self.assertEqual(self.dajaxice.clean_method('other'), 'POST') + + def test_modules(self): + self.dajaxice.register(self.function, 'foo') + self.dajaxice.register(self.function, 'bar') + + self.assertEqual(type(self.dajaxice.modules), DajaxiceModule) + self.assertEqual(self.dajaxice.modules.functions.keys(), ['foo', 'bar']) + + +class DajaxiceConfigTest(TestCase): + + def setUp(self): + self.config = DajaxiceConfig() + + def test_defaults(self): + self.assertTrue(self.config.DAJAXICE_XMLHTTPREQUEST_JS_IMPORT) + self.assertTrue(self.config.DAJAXICE_JSON2_JS_IMPORT) + self.assertEqual(self.config.DAJAXICE_EXCEPTION, 'DAJAXICE_EXCEPTION') + self.assertEqual(self.config.DAJAXICE_MEDIA_PREFIX, 'dajaxice') + + dajaxice_url = r'^%s/' % self.config.DAJAXICE_MEDIA_PREFIX + self.assertEqual(self.config.dajaxice_url, dajaxice_url) + + self.assertEqual(type(self.config.modules), DajaxiceModule) class DjangoIntegrationTest(TestCase): @@ -48,127 +116,50 @@ class DjangoIntegrationTest(TestCase): urls = 'dajaxice.tests.urls' def setUp(self): - settings.DAJAXICE_MEDIA_PREFIX = "dajaxice" - settings.DAJAXICE_DEBUG = False - settings.INSTALLED_APPS += ('dajaxice.tests', 'dajaxice.tests.submodules',) - os.environ['DJANGO_SETTINGS_MODULE'] = 'dajaxice' + settings.INSTALLED_APPS += ('dajaxice.tests',) def test_calling_not_registered_function(self): - self.failUnlessRaises(FunctionNotCallableError, self.client.post, '/dajaxice/dajaxice.tests.this_function_not_exist/', {'callback': 'my_callback'}) + self.failUnlessRaises(FunctionNotCallableError, self.client.post, '/dajaxice/dajaxice.tests.this_function_not_exist/') def test_calling_registered_function(self): - response = self.client.post('/dajaxice/dajaxice.tests.test_foo/', {'callback': 'my_callback'}) + response = self.client.post('/dajaxice/dajaxice.tests.test_foo/') self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, 'my_callback()') + self.failUnlessEqual(response.content, '{"foo": "bar"}') def test_calling_registered_function_with_params(self): - response = self.client.post('/dajaxice/dajaxice.tests.test_foo_with_params/', {'callback': 'my_callback', 'argv': '{"param1":"value1"}'}) + response = self.client.post('/dajaxice/dajaxice.tests.test_foo_with_params/', {'argv': '{"param1":"value1"}'}) self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, 'my_callback("value1")') + self.failUnlessEqual(response.content, '{"param1": "value1"}') def test_bad_function(self): - response = self.client.post('/dajaxice/dajaxice.tests.test_ajax_exception/', {'callback': 'my_callback'}) + response = self.client.post('/dajaxice/dajaxice.tests.test_ajax_exception/') self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, "my_callback('DAJAXICE_EXCEPTION')") + self.failUnlessEqual(response.content, "DAJAXICE_EXCEPTION") - def test_is_callable(self): + def test_get_register(self): - dr = DajaxiceRequest(None, 'dajaxice.tests.test_registered_function') - self.failUnless(dr._is_callable()) + response = self.client.get('/dajaxice/dajaxice.tests.test_get_register/') - dr = DajaxiceRequest(None, 'dajaxice.tests.test_ajax_not_registered') - self.failIf(dr._is_callable()) + self.failUnlessEqual(response.status_code, 200) + self.failUnlessEqual(response.content, '{"foo": "user"}') - def test_get_js_functions(self): + def test_get_custom_name_register(self): - js_functions = DajaxiceRequest.get_js_functions() + response = self.client.get('/dajaxice/get_user_data/') - functions = [DajaxiceFunction('test_registered_function', 'dajaxice.tests.ajax.test_registered_function'), - DajaxiceFunction('test_string', 'dajaxice.tests.ajax.test_string'), - DajaxiceFunction('test_ajax_exception', 'dajaxice.tests.ajax.test_ajax_exception'), - DajaxiceFunction('test_foo', 'dajaxice.tests.ajax.test_foo'), - DajaxiceFunction('test_foo_with_params', 'dajaxice.tests.ajax.test_foo_with_params'), - DajaxiceFunction('test_submodule_registered_function', 'dajaxice.tests.submodules.ajax.test_submodule_registered_function')] + self.failUnlessEqual(response.status_code, 200) + self.failUnlessEqual(response.content, '{"bar": "user"}') - callables = [f.path for f in functions] + def test_multi_register(self): - self.failUnlessEqual(len(js_functions), 1) - self.failUnlessEqual(dajaxice_functions._callable, callables) + response = self.client.get('/dajaxice/get_multi/') + self.failUnlessEqual(response.status_code, 200) + self.failUnlessEqual(response.content, '{"foo": "multi"}') - sub = js_functions[0] - self.failUnlessEqual(len(sub.sub_modules), 1) - self.failUnlessEqual(len(sub.functions), 0) - self.failUnlessEqual(sub.name, 'dajaxice') - - sub = js_functions[0].sub_modules[0] - self.failUnlessEqual(len(sub.sub_modules), 1) - self.failUnlessEqual(len(sub.functions), 5) - self.failUnlessEqual(sub.functions, functions[:-1]) - self.failUnlessEqual(sub.name, 'tests') - - sub = js_functions[0].sub_modules[0].sub_modules[0] - self.failUnlessEqual(len(sub.sub_modules), 0) - self.failUnlessEqual(len(sub.functions), 1) - self.failUnlessEqual(sub.functions, functions[-1:]) - self.failUnlessEqual(sub.name, 'submodules') - - def test_get_ajax_function(self): - - # Test modern Import with a real ajax function - dr = DajaxiceRequest(None, 'dajaxice.tests.test_foo') - function = dr._modern_get_ajax_function() - self.failUnless(hasattr(function, '__call__')) - - # Test modern Import without a real ajax function - dr = DajaxiceRequest(None, 'dajaxice.tests.test_foo2') - self.failUnlessRaises(DajaxiceImportError, dr._modern_get_ajax_function) - - -class DajaxiceFunctionTest(unittest.TestCase): - - def setUp(self): - self.function = DajaxiceFunction('my_function', 'module.submodule.foo.ajax') - - def test_constructor(self): - self.failUnlessEqual(self.function.name, 'my_function') - self.failUnlessEqual(self.function.path, 'module.submodule.foo.ajax') - - def test_get_callable_path(self): - self.failUnlessEqual(self.function.get_callable_path(), 'module.submodule.foo.my_function') - - -class DajaxiceModuleTest(unittest.TestCase): - - def setUp(self): - self.module = DajaxiceModule('module.submodule.foo.ajax'.split('.')) - - def test_constructor(self): - self.failUnlessEqual(self.module.functions, []) - self.failUnlessEqual(self.module.name, 'module') - - self.failUnlessEqual(len(self.module.sub_modules), 1) - - def test_add_function(self): - function = DajaxiceFunction('my_function', 'module.submodule.foo.ajax') - self.failUnlessEqual(len(self.module.functions), 0) - self.module.add_function(function) - self.failUnlessEqual(len(self.module.functions), 1) - - def test_has_sub_modules(self): - self.failUnlessEqual(self.module.has_sub_modules(), True) - - def test_exist_submodule(self): - self.failUnlessEqual(self.module.exist_submodule('submodule'), 0) - self.assertFalse(self.module.exist_submodule('other')) - self.module.add_submodule('other.foo'.split('.')) - self.failUnlessEqual(self.module.exist_submodule('other'), 1) - - def test_add_submodule(self): - self.failUnlessEqual(len(self.module.sub_modules), 1) - self.module.add_submodule('other.foo'.split('.')) - self.failUnlessEqual(len(self.module.sub_modules), 2) - self.assertTrue(type(self.module.sub_modules[1]), DajaxiceModule) + response = self.client.post('/dajaxice/post_multi/') + self.failUnlessEqual(response.status_code, 200) + self.failUnlessEqual(response.content, '{"foo": "multi"}') diff --git a/dajaxice/tests/ajax.py b/dajaxice/tests/ajax.py index c0a27fb91..7d62d2e88 100644 --- a/dajaxice/tests/ajax.py +++ b/dajaxice/tests/ajax.py @@ -1,61 +1,43 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - -from django.utils import simplejson -from dajaxice.core import dajaxice_functions +import json +from dajaxice.decorators import dajaxice_register +@dajaxice_register def test_registered_function(request): return "" -dajaxice_functions.register(test_registered_function) +@dajaxice_register def test_string(request): - return simplejson.dumps({'string': 'hello world'}) -dajaxice_functions.register(test_string) + return json.dumps({'string': 'hello world'}) +@dajaxice_register def test_ajax_exception(request): raise Exception() - return -dajaxice_functions.register(test_ajax_exception) +@dajaxice_register def test_foo(request): - return "" -dajaxice_functions.register(test_foo) + return json.dumps({'foo': 'bar'}) +@dajaxice_register def test_foo_with_params(request, param1): - return simplejson.dumps(param1) -dajaxice_functions.register(test_foo_with_params) + return json.dumps({'param1': param1}) + + +@dajaxice_register(method='GET') +def test_get_register(request): + return json.dumps({'foo': 'user'}) + + +@dajaxice_register(method='GET', name="get_user_data") +def test_get_with_name_register(request): + return json.dumps({'bar': 'user'}) + + +@dajaxice_register(method='GET', name="get_multi") +@dajaxice_register(name="post_multi") +def test_multi_register(request): + return json.dumps({'foo': 'multi'}) diff --git a/dajaxice/tests/submodules/__init__.py b/dajaxice/tests/submodules/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/dajaxice/tests/submodules/ajax.py b/dajaxice/tests/submodules/ajax.py deleted file mode 100644 index d3028347c..000000000 --- a/dajaxice/tests/submodules/ajax.py +++ /dev/null @@ -1,40 +0,0 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - - -from dajaxice.core import dajaxice_functions - - -def test_submodule_registered_function(request): - return "" -dajaxice_functions.register(test_submodule_registered_function) diff --git a/dajaxice/tests/urls.py b/dajaxice/tests/urls.py index bb088f8f3..fb95d23c1 100644 --- a/dajaxice/tests/urls.py +++ b/dajaxice/tests/urls.py @@ -1,44 +1,10 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - -from django.conf.urls import * -from django.conf import settings -from dajaxice.core import dajaxice_autodiscover +from django.conf.urls.defaults import * +from dajaxice.core import dajaxice_autodiscover, dajaxice_config dajaxice_autodiscover() urlpatterns = patterns('', #Dajaxice URLS - (r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, include('dajaxice.urls')), + url(dajaxice_config.dajaxice_url, include('dajaxice.urls')), ) diff --git a/dajaxice/urls.py b/dajaxice/urls.py index 167ad7a99..767dc500b 100644 --- a/dajaxice/urls.py +++ b/dajaxice/urls.py @@ -1,39 +1,7 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- - from django.conf.urls import * +from .views import DajaxiceRequest - -urlpatterns = patterns('', - url(r'^dajaxice.core.js$', 'dajaxice.views.js_core'), - url(r'^(.*)/$', 'dajaxice.views.dajaxice_request'),) +urlpatterns = patterns('dajaxice.views', + url(r'^(.+)/$', DajaxiceRequest.as_view(), name='dajaxice-call-endpoint'), + url(r'', DajaxiceRequest.as_view(), name='dajaxice-endpoint'), +) diff --git a/dajaxice/utils.py b/dajaxice/utils.py index f2bd976ee..a02d2cd6b 100644 --- a/dajaxice/utils.py +++ b/dajaxice/utils.py @@ -1,50 +1,8 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- +from django.http import QueryDict def deserialize_form(data): """ Create a new QueryDict from a serialized form. """ - from django.http import QueryDict - data = QueryDict(query_string=unicode(data).encode('utf-8')) - return data - - -def simple_import_module(name): - """ - Reduced version of import_module - """ - import sys - __import__(name) - return sys.modules[name] + return QueryDict(query_string=unicode(data).encode('utf-8')) diff --git a/dajaxice/views.py b/dajaxice/views.py index f6ee83c61..4e656d3fd 100644 --- a/dajaxice/views.py +++ b/dajaxice/views.py @@ -1,62 +1,59 @@ -#---------------------------------------------------------------------- -# Copyright (c) 2009-2011 Benito Jorge Bastida -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# o Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the disclaimer that follows. -# -# o Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions, and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# -# o Neither the name of Digital Creations nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS -# IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL -# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -#---------------------------------------------------------------------- +import logging, json -from django.shortcuts import render_to_response -from django.views.decorators.cache import cache_control +from django.conf import settings +from django.views.generic.base import View +from django.http import HttpResponse, Http404 -from dajaxice.core import DajaxiceRequest +from dajaxice.exceptions import FunctionNotCallableError +from dajaxice.core import dajaxice_functions, dajaxice_config + +log = logging.getLogger('dajaxice') -def dajaxice_request(request, call): +def safe_dict(d): """ - dajaxice_request - Uses DajaxRequest to handle dajax request. - Return the apropiate json according app_name and method. + Recursively clone json structure with UTF-8 dictionary keys + http://www.gossamer-threads.com/lists/python/bugs/684379 """ - return DajaxiceRequest(request, call).process() + if isinstance(d, dict): + return dict([(k.encode('utf-8'), safe_dict(v)) for k, v in d.iteritems()]) + elif isinstance(d, list): + return [safe_dict(x) for x in d] + else: + return d -@cache_control(max_age=DajaxiceRequest.get_cache_control()) -def js_core(request): - """ - Return the dajax JS code according settings.DAJAXICE_FUNCTIONS - registered functions. - """ - data = {'dajaxice_js_functions': DajaxiceRequest.get_js_functions(), - 'DAJAXICE_URL_PREFIX': DajaxiceRequest.get_media_prefix(), - 'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT': DajaxiceRequest.get_xmlhttprequest_js_import(), - 'DAJAXICE_JSON2_JS_IMPORT': DajaxiceRequest.get_json2_js_import(), - 'DAJAXICE_EXCEPTION': DajaxiceRequest.get_exception_message(), - 'DAJAXICE_JS_DOCSTRINGS': DajaxiceRequest.get_js_docstrings()} +class DajaxiceRequest(View): + """ Handle all the dajaxice xhr requests. """ - return render_to_response('dajaxice/dajaxice.core.js', data, mimetype="text/javascript") + def dispatch(self, request, name=None): + + if not name: + raise Http404 + + # Check if the function is callable + if dajaxice_functions.is_callable(name, request.method): + + function = dajaxice_functions.get(name) + data = getattr(request, function.method).get('argv', '') + + # Clean the argv + if data != 'undefined': + try: + data = safe_dict(json.loads(data)) + except Exception: + data = {} + else: + data = {} + + # Call the function. If something goes wrong, handle the Exception + try: + response = function.call(request, **data) + except Exception: + if settings.DEBUG: + raise + response = dajaxice_config.DAJAXICE_EXCEPTION + + return HttpResponse(response, mimetype="application/x-json") + else: + raise FunctionNotCallableError(name) diff --git a/ietf/meeting/ajax.py b/ietf/meeting/ajax.py index 93545f604..4de0cba59 100644 --- a/ietf/meeting/ajax.py +++ b/ietf/meeting/ajax.py @@ -14,6 +14,12 @@ from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint import debug +def dajaxice_core_js(request): + # this is a slightly weird hack to get, we seem to need this because + # we're not using the built-in static files support + from dajaxice.finders import DajaxiceStorage + return HttpResponse(DajaxiceStorage().dajaxice_core_js(), content_type="application/javascript") + @dajaxice_register def readonly(request, meeting_num, schedule_id): meeting = get_meeting(meeting_num) diff --git a/ietf/urls.py b/ietf/urls.py index 333d60144..3ab4e5221 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -63,6 +63,7 @@ urlpatterns = patterns('', # Google webmaster tools verification url (r'^googlea30ad1dacffb5e5b.html', TemplateView.as_view(template_name='googlea30ad1dacffb5e5b.html')), + (r'^%s/dajaxice.core.js' % settings.DAJAXICE_MEDIA_PREFIX, 'ietf.meeting.ajax.dajaxice_core_js'), (r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, include('dajaxice.urls')), )