From fc512680d8b25e03452b58ed8fc5dd6dd85bff9c Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Thu, 31 Jul 2014 22:13:01 +0000 Subject: [PATCH] From mcr@sandelman.ca: Refactor to remove dajaxice. Modified the URLs that address schedules: - urls now always start with /agenda, removing the mix of /agenda and /schedule prefixes - urls to a given schedule include the schedule owner (by email address) Corrected issue that prevented deleting sessions. Changed comment mechanism in timeslot_edit.js Migration to change the email address for (System) Fixes bug #1426 Commit ready to merge - Legacy-Id: 8229 --- dajaxice/__init__.py | 1 - dajaxice/core/Dajaxice.py | 137 -------- dajaxice/core/__init__.py | 38 --- dajaxice/decorators.py | 48 --- dajaxice/exceptions.py | 10 - dajaxice/finders.py | 75 ---- dajaxice/models.py | 1 - dajaxice/templates/dajaxice/dajaxice.core.js | 145 -------- .../dajaxice/dajaxice_function_loop.js | 5 - .../dajaxice/dajaxice_module_loop.js | 11 - .../templatetags/dajaxice_templatetags.py | 35 -- dajaxice/tests/__init__.py | 165 --------- dajaxice/tests/ajax.py | 43 --- dajaxice/tests/requirements.txt | 1 - dajaxice/tests/urls.py | 10 - dajaxice/urls.py | 7 - dajaxice/utils.py | 8 - dajaxice/views.py | 60 ---- ietf/meeting/ajax.py | 322 ++++++++---------- ietf/meeting/helpers.py | 24 +- ietf/meeting/models.py | 7 +- ietf/meeting/tests_api.py | 118 +++---- ietf/meeting/tests_js.py | 2 +- ietf/meeting/tests_views.py | 16 +- ietf/meeting/urls.py | 19 +- ietf/meeting/views.py | 47 +-- .../migrations/0001_change_system_email.py | 91 +++++ .../person/migrations}/__init__.py | 0 ietf/settings.py | 4 - ietf/templates/meeting/agenda_list.html | 8 +- ietf/templates/meeting/landscape_edit.html | 7 +- ietf/templates/meeting/private_agenda.html | 2 - ietf/templates/meeting/properties_edit.html | 4 - ietf/templates/meeting/room_edit.html | 4 - ietf/templates/meeting/timeslot_edit.html | 12 +- ietf/urls.py | 5 - static/js/agenda/agenda_edit.js | 25 +- static/js/agenda/agenda_listeners.js | 51 +-- static/js/agenda/agenda_objects.js | 22 ++ static/js/agenda/agenda_property_utils.js | 2 +- static/js/agenda/timeslot_edit.js | 126 +++---- static/test/agenda_ui.html | 2 - 42 files changed, 466 insertions(+), 1254 deletions(-) delete mode 100644 dajaxice/__init__.py delete mode 100644 dajaxice/core/Dajaxice.py delete mode 100644 dajaxice/core/__init__.py delete mode 100644 dajaxice/decorators.py delete mode 100644 dajaxice/exceptions.py delete mode 100644 dajaxice/finders.py delete mode 100644 dajaxice/models.py delete mode 100644 dajaxice/templates/dajaxice/dajaxice.core.js delete mode 100644 dajaxice/templates/dajaxice/dajaxice_function_loop.js delete mode 100644 dajaxice/templates/dajaxice/dajaxice_module_loop.js delete mode 100644 dajaxice/templatetags/dajaxice_templatetags.py delete mode 100644 dajaxice/tests/__init__.py delete mode 100644 dajaxice/tests/ajax.py delete mode 100644 dajaxice/tests/requirements.txt delete mode 100644 dajaxice/tests/urls.py delete mode 100644 dajaxice/urls.py delete mode 100644 dajaxice/utils.py delete mode 100644 dajaxice/views.py create mode 100644 ietf/person/migrations/0001_change_system_email.py rename {dajaxice/templatetags => ietf/person/migrations}/__init__.py (100%) diff --git a/dajaxice/__init__.py b/dajaxice/__init__.py deleted file mode 100644 index bf70dc73f..000000000 --- a/dajaxice/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = (0, 5, 5, 'beta') diff --git a/dajaxice/core/Dajaxice.py b/dajaxice/core/Dajaxice.py deleted file mode 100644 index 86aa6e0ed..000000000 --- a/dajaxice/core/Dajaxice.py +++ /dev/null @@ -1,137 +0,0 @@ -import logging - -from django.utils.importlib import import_module - -log = logging.getLogger('dajaxice') - - -class DajaxiceFunction(object): - """ Basic representation of a dajaxice ajax function.""" - - def __init__(self, function, name, method): - self.function = function - self.name = name - self.method = method - - def call(self, *args, **kwargs): - """ Call the function. """ - return self.function(*args, **kwargs) - - -class DajaxiceModule(object): - """ Basic representation of a dajaxice module. """ - - def __init__(self, name=None): - self.name = name - self.functions = {} - self.submodules = {} - - 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.""" - - # 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: - self.functions[name] = function - - -class Dajaxice(object): - - def __init__(self): - self._registry = {} - self._modules = None - - def register(self, function, name=None, method='POST'): - """ - Register this function as a dajaxice function. - - 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 - - # Check for already registered functions - if name in self._registry: - log.error('%s was already registered.' % name) - return - - # Create the dajaxice function. - function = DajaxiceFunction(function=function, - name=name, - method=method) - - # Register this new ajax function - self._registry[name] = function - - def is_callable(self, name, method): - """ Return if the function callable or not. """ - return name in self._registry and self._registry[name].method == method - - def clean_method(self, method): - """ Clean the http method. """ - method = method.upper() - if method not in ['GET', 'POST']: - method = 'POST' - return method - - 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 - - -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 - """ - global LOADING_DAJAXICE - if LOADING_DAJAXICE: - return - LOADING_DAJAXICE = True - - import imp - from django.conf import settings - - for app in settings.INSTALLED_APPS: - - try: - app_path = import_module(app).__path__ - except AttributeError: - continue - - try: - imp.find_module('ajax', app_path) - except ImportError: - continue - - import_module("%s.ajax" % app) - - LOADING_DAJAXICE = False diff --git a/dajaxice/core/__init__.py b/dajaxice/core/__init__.py deleted file mode 100644 index d87543fba..000000000 --- a/dajaxice/core/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -from django.conf import settings - -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 deleted file mode 100644 index 44df89ead..000000000 --- a/dajaxice/decorators.py +++ /dev/null @@ -1,48 +0,0 @@ -import functools - -from dajaxice.core import dajaxice_functions - - -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): - ... - """ - - 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 deleted file mode 100644 index 139ba6282..000000000 --- a/dajaxice/exceptions.py +++ /dev/null @@ -1,10 +0,0 @@ -class DajaxiceError(Exception): - pass - - -class FunctionNotCallableError(DajaxiceError): - pass - - -class DajaxiceImportError(DajaxiceError): - pass diff --git a/dajaxice/finders.py b/dajaxice/finders.py deleted file mode 100644 index 5178b22e5..000000000 --- a/dajaxice/finders.py +++ /dev/null @@ -1,75 +0,0 @@ -import os -import tempfile - -from django.contrib.staticfiles import finders -from django.template import Context -from django.template.loader import get_template -from django.core.exceptions import SuspiciousOperation - - -class VirtualStorage(finders.FileSystemStorage): - """" Mock a FileSystemStorage to build tmp files on demand.""" - - def __init__(self, *args, **kwargs): - self._files_cache = {} - super(VirtualStorage, self).__init__(*args, **kwargs) - - def get_or_create_file(self, path): - if path not in self.files: - return '' - - data = getattr(self, self.files[path])() - - try: - current_file = open(self._files_cache[path]) - current_data = current_file.read() - current_file.close() - if current_data != data: - os.remove(path) - raise Exception("Invalid data") - except Exception: - handle, tmp_path = tempfile.mkstemp() - tmp_file = open(tmp_path, 'w') - tmp_file.write(data) - tmp_file.close() - self._files_cache[path] = tmp_path - - return self._files_cache[path] - - def exists(self, name): - return name in self.files - - def listdir(self, path): - folders, files = [], [] - for f in self.files: - if f.startswith(path): - f = f.replace(path, '', 1) - if os.sep in f: - folders.append(f.split(os.sep, 1)[0]) - else: - files.append(f) - return folders, files - - def path(self, name): - try: - path = self.get_or_create_file(name) - except ValueError: - raise SuspiciousOperation("Attempted access to '%s' denied." % name) - return os.path.normpath(path) - - -class DajaxiceStorage(VirtualStorage): - - files = {os.path.join('dajaxice', 'dajaxice.core.js'): 'dajaxice_core_js'} - - def dajaxice_core_js(self): - from dajaxice.core import dajaxice_autodiscover, dajaxice_config - - dajaxice_autodiscover() - - c = Context({'dajaxice_config': dajaxice_config}) - return get_template(os.path.join('dajaxice', 'dajaxice.core.js')).render(c) - - -class DajaxiceFinder(finders.BaseStorageFinder): - storage = DajaxiceStorage() diff --git a/dajaxice/models.py b/dajaxice/models.py deleted file mode 100644 index 36e89fccb..000000000 --- a/dajaxice/models.py +++ /dev/null @@ -1 +0,0 @@ -# Don't delete me diff --git a/dajaxice/templates/dajaxice/dajaxice.core.js b/dajaxice/templates/dajaxice/dajaxice.core.js deleted file mode 100644 index a6ceed561..000000000 --- a/dajaxice/templates/dajaxice/dajaxice.core.js +++ /dev/null @@ -1,145 +0,0 @@ -{% load url from future %} -var Dajaxice = { - - {% 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; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = cookies[i].toString().replace(/^\s+/, "").replace(/\s+$/, ""); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - }, - - call: function(dajaxice_function, method, dajaxice_callback, argv, custom_settings) - { - 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']; - } - - var send_data = 'argv='+encodeURIComponent(JSON.stringify(argv)), - oXMLHttpRequest = new XMLHttpRequest, - endpoint = '{% url 'dajaxice-endpoint' %}'+dajaxice_function+'/'; - - if(method == 'GET'){ - endpoint = endpoint + '?' + send_data; - } - 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('{{ dajaxice_config.django_settings.CSRF_COOKIE_NAME }}')); - oXMLHttpRequest.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if(this.responseText == Dajaxice.EXCEPTION || !(this.status in Dajaxice.valid_http_responses())){ - error_callback(); - } - else{ - var response; - try { - response = JSON.parse(this.responseText); - } - catch (exception) { - response = this.responseText; - } - dajaxice_callback(response); - } - } - } - 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 Dajaxice.default_settings[key]; - } - return this.settings[key]; - }, - - 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_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 %} -/* - http://www.json.org/json2.js Compiled with Google Closure - This code was released under Public Domain by json.org , Thanks! - I hope that in the future this code won't be neccessary, but today all browsers doesn't supports JSON.stringify(). -*/ -{% endcomment %} -{% if dajaxice_config.DAJAXICE_JSON2_JS_IMPORT %} -var JSON;JSON||(JSON={}); -(function(){function k(a){return 10>a?"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' % url diff --git a/dajaxice/tests/__init__.py b/dajaxice/tests/__init__.py deleted file mode 100644 index fb918e59e..000000000 --- a/dajaxice/tests/__init__.py +++ /dev/null @@ -1,165 +0,0 @@ -from django.test import TestCase - -from django.conf import settings - -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): - - urls = 'dajaxice.tests.urls' - - def setUp(self): - 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/') - - def test_calling_registered_function(self): - response = self.client.post('/dajaxice/dajaxice.tests.test_foo/') - - self.failUnlessEqual(response.status_code, 200) - 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/', {'argv': '{"param1":"value1"}'}) - - self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, '{"param1": "value1"}') - - def test_bad_function(self): - - response = self.client.post('/dajaxice/dajaxice.tests.test_ajax_exception/') - self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, "DAJAXICE_EXCEPTION") - - def test_get_register(self): - - response = self.client.get('/dajaxice/dajaxice.tests.test_get_register/') - - self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, '{"foo": "user"}') - - def test_get_custom_name_register(self): - - response = self.client.get('/dajaxice/get_user_data/') - - self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, '{"bar": "user"}') - - def test_multi_register(self): - - response = self.client.get('/dajaxice/get_multi/') - self.failUnlessEqual(response.status_code, 200) - self.failUnlessEqual(response.content, '{"foo": "multi"}') - - 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 deleted file mode 100644 index 0fe1360bd..000000000 --- a/dajaxice/tests/ajax.py +++ /dev/null @@ -1,43 +0,0 @@ -from django.utils import simplejson -from dajaxice.decorators import dajaxice_register - - -@dajaxice_register -def test_registered_function(request): - return "" - - -@dajaxice_register -def test_string(request): - return simplejson.dumps({'string': 'hello world'}) - - -@dajaxice_register -def test_ajax_exception(request): - raise Exception() - - -@dajaxice_register -def test_foo(request): - return simplejson.dumps({'foo': 'bar'}) - - -@dajaxice_register -def test_foo_with_params(request, param1): - return simplejson.dumps({'param1': param1}) - - -@dajaxice_register(method='GET') -def test_get_register(request): - return simplejson.dumps({'foo': 'user'}) - - -@dajaxice_register(method='GET', name="get_user_data") -def test_get_with_name_register(request): - return simplejson.dumps({'bar': 'user'}) - - -@dajaxice_register(method='GET', name="get_multi") -@dajaxice_register(name="post_multi") -def test_multi_register(request): - return simplejson.dumps({'foo': 'multi'}) diff --git a/dajaxice/tests/requirements.txt b/dajaxice/tests/requirements.txt deleted file mode 100644 index 94a0e8344..000000000 --- a/dajaxice/tests/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Django diff --git a/dajaxice/tests/urls.py b/dajaxice/tests/urls.py deleted file mode 100644 index fb95d23c1..000000000 --- a/dajaxice/tests/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls.defaults import * - -from dajaxice.core import dajaxice_autodiscover, dajaxice_config - -dajaxice_autodiscover() - -urlpatterns = patterns('', - #Dajaxice URLS - url(dajaxice_config.dajaxice_url, include('dajaxice.urls')), -) diff --git a/dajaxice/urls.py b/dajaxice/urls.py deleted file mode 100644 index c920d1991..000000000 --- a/dajaxice/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.conf.urls import patterns, url, include -from .views import DajaxiceRequest - -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 deleted file mode 100644 index a02d2cd6b..000000000 --- a/dajaxice/utils.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.http import QueryDict - - -def deserialize_form(data): - """ - Create a new QueryDict from a serialized form. - """ - return QueryDict(query_string=unicode(data).encode('utf-8')) diff --git a/dajaxice/views.py b/dajaxice/views.py deleted file mode 100644 index b25c6850c..000000000 --- a/dajaxice/views.py +++ /dev/null @@ -1,60 +0,0 @@ -import logging - -from django.conf import settings -import json -from django.views.generic.base import View -from django.http import HttpResponse, Http404 - -from dajaxice.exceptions import FunctionNotCallableError -from dajaxice.core import dajaxice_functions, dajaxice_config - -log = logging.getLogger('dajaxice') - - -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(View): - """ Handle all the dajaxice xhr requests. """ - - 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, content_type="application/x-json") - else: - raise FunctionNotCallableError(name) diff --git a/ietf/meeting/ajax.py b/ietf/meeting/ajax.py index 308fecf34..0465f3d23 100644 --- a/ietf/meeting/ajax.py +++ b/ietf/meeting/ajax.py @@ -1,136 +1,72 @@ import json -import datetime from django.shortcuts import get_object_or_404, redirect from django.http import HttpResponse +from django.http import QueryDict +from django.http import Http404 from django.views.decorators.http import require_POST -from dajaxice.decorators import dajaxice_register - -from ietf.ietfauth.utils import role_required, has_role, user_is_person -from ietf.meeting.helpers import get_meeting, get_schedule, get_schedule_by_id, agenda_permissions +from ietf.ietfauth.utils import role_required, has_role +from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions, get_person_by_email, get_schedule_by_name from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint, ScheduledSession, ResourceAssociation from ietf.meeting.views import edit_timeslots, edit_agenda -from ietf.name.models import TimeSlotTypeName import debug # pyflakes:ignore -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") +def is_truthy_enough(value): + return not (value == "0" or value == 0 or value=="false") -@dajaxice_register -def readonly(request, meeting_num, schedule_id): - meeting = get_meeting(meeting_num) - schedule = get_schedule_by_id(meeting, schedule_id) +# look up a schedule by number, owner and schedule name, returning an API error if it can not be found +def get_meeting_schedule(num, owner, name): + meeting = get_meeting(num) + person = get_person_by_email(owner) + schedule = get_schedule_by_name(meeting, person, name) + if schedule is None or person is None or meeting is None: + meeting_pk = 0 + person_pk = 0 + schedule_pk =0 + # to make diagnostics more meaningful, log what we found + if meeting: + meeting_pk = meeting.pk + if person: + person_pk = person.pk + if schedule: + schedule_pk=schedule.pk + return HttpResponse(json.dumps({'error' : 'invalid meeting=%s/person=%s/schedule=%s' % (num,owner,name), + 'meeting': meeting_pk, + 'person': person_pk, + 'schedule': schedule_pk}), + content_type="application/json", + status=404); + return meeting, person, schedule + + + +# should asking if an agenda is read-only require any kind of permission? +def agenda_permission_api(request, num, owner, name): + meeting = get_meeting(num) + person = get_person_by_email(owner) + schedule = get_schedule_by_name(meeting, person, name) + + save_perm = False secretariat = False - write_perm = False + cansee = False + canedit = False + owner_href = "" - cansee,canedit = agenda_permissions(meeting, schedule, request.user) - read_only = not canedit + if schedule is not None: + cansee,canedit,secretariat = agenda_permissions(meeting, schedule, request.user) + owner_href = request.build_absolute_uri(schedule.owner.json_url()) - user = request.user - if has_role(user, "Secretariat"): - secretariat = True - write_perm = True + if has_role(request.user, "Area Director") or secretariat: + save_perm = True - if has_role(user, "Area Director"): - write_perm = True - - if user_is_person(user, schedule.owner): - read_only = False - - # FIXME: the naming here needs improvement, one can have - # read_only == True and write_perm == True? - - return json.dumps( - {'secretariat': secretariat, - 'write_perm': write_perm, - 'owner_href': request.build_absolute_uri(schedule.owner.json_url()), - 'read_only': read_only}) - -@dajaxice_register -def update_timeslot_pinned(request, schedule_id, scheduledsession_id, pinned=False): - - if not has_role(request.user,('Area Director','Secretariat')): - return json.dumps({'error':'no permission'}) - - schedule = get_object_or_404(Schedule, pk = int(schedule_id)) - meeting = schedule.meeting - cansee,canedit = agenda_permissions(meeting, schedule, request.user) - - if not canedit: - return json.dumps({'error':'no permission'}) - - if scheduledsession_id is not None: - ss_id = int(scheduledsession_id) - - if ss_id == 0: - return json.dumps({'error':'no permission'}) - - ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id) - ss.pinned = pinned - ss.save() - - return json.dumps({'message':'valid'}) - -@dajaxice_register -def update_timeslot_purpose(request, - meeting_num, - timeslot_id=None, - purpose =None, - room_id = None, - duration= None, - time = None): - - if not has_role(request.user,'Secretariat'): - return json.dumps({'error':'no permission'}) - - meeting = get_meeting(meeting_num) - ts_id = int(timeslot_id) - time_str = time - if ts_id == 0: - try: - time = datetime.datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S') - except: - return json.dumps({'error':'invalid time: %s' % (time_str)}) - - try: - room = meeting.room_set.get(pk = int(room_id)) - except Room.DoesNotExist: - return json.dumps({'error':'invalid room id'}) - - timeslot = TimeSlot(meeting=meeting, - location = room, - time = time, - duration = duration) - else: - try: - timeslot = TimeSlot.objects.get(pk=ts_id) - except: - return json.dumps({'error':'invalid timeslot'}) - - try: - timeslottypename = TimeSlotTypeName.objects.get(pk = purpose) - except: - return json.dumps({'error':'invalid timeslot type', - 'extra': purpose}) - - timeslot.type = timeslottypename - try: - timeslot.save() - except: - return json.dumps({'error':'failed to save'}) - - try: - # really should return 201 created, but dajaxice sucks. - json_dict = timeslot.json_dict(request.build_absolute_uri('/')) - return json.dumps(json_dict) - except: - return json.dumps({'error':'failed to save'}) + return HttpResponse(json.dumps({'secretariat': secretariat, + 'save_perm': save_perm, + 'read_only': canedit==False, + 'owner_href': owner_href}), + content_type="application/json") ############################################################################# ## ROOM API @@ -239,16 +175,43 @@ def timeslot_addslot(request, meeting): newslot.meeting = meeting newslot.save() - newslot.create_concurrent_timeslots() + # no longer create concurrent timeslots, because they will default, when there is + # no timeslots, to unavailable, which can be created later on. + # newslot.create_concurrent_timeslots() # XXX FIXME: timeslot_dayurl is undefined. Placeholder: - timeslot_dayurl = None + # timeslot_dayurl = None # XXX FIXME: newroom is undefined. Placeholder: - newroom = None - if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']: - return redirect(timeslot_dayurl, meeting.number, newroom.pk) + # newroom = None + values = newslot.json_dict(request.build_absolute_uri('/')) + response = HttpResponse(json.dumps(values), + content_type="application/json", + status=201) + response['Location'] = values['href'] + return response + +@role_required('Secretariat') +def timeslot_updslot(request, meeting, slotid): + slot = get_object_or_404(meeting.timeslot_set, pk=slotid) + + # at present, updates to the purpose only is supported. + # updates to time or duration would need likely need to be + # propogated to the entire vertical part of the grid, and nothing + # needs to do that yet. + if request.method == 'POST': + put_vars = request.POST + slot.type_id = put_vars["purpose"] else: - return redirect(edit_timeslots, meeting.number) + put_vars = QueryDict(request.body) + slot.type_id = put_vars.get("purpose") + + slot.save() + + # need to return the new object. + dict1 = slot.json_dict(request.build_absolute_uri('/')) + dict1['message'] = 'valid' + return HttpResponse(json.dumps(dict1), + content_type="application/json") @role_required('Secretariat') def timeslot_delslot(request, meeting, slotid): @@ -276,10 +239,8 @@ def timeslot_sloturl(request, num=None, slotid=None): slot = get_object_or_404(meeting.timeslot_set, pk=slotid) return HttpResponse(json.dumps(slot.json_dict(request.build_absolute_uri('/'))), content_type="application/json") - elif request.method == 'POST': - # not yet implemented! - #return timeslot_updslot(request, meeting) - return HttpResponse(status=406) + elif request.method == 'POST' or request.method == 'PUT': + return timeslot_updslot(request, meeting, slotid) elif request.method == 'DELETE': return timeslot_delslot(request, meeting, slotid) @@ -311,9 +272,9 @@ def agenda_add(request, meeting): newagenda.save() if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']: - return redirect(agenda_infourl, meeting.number, newagenda.name) + return redirect(agenda_infourl, meeting.number, newagenda.owner_email(), newagenda.name) else: - return redirect(edit_agenda, meeting.number, newagenda.name) + return redirect(edit_agenda, meeting.number, newagenda.owner_email(), newagenda.name) @require_POST def agenda_update(request, meeting, schedule): @@ -325,16 +286,13 @@ def agenda_update(request, meeting, schedule): if not user.is_authenticated(): return HttpResponse({'error':'no permission'}, status=403) - cansee,canedit = agenda_permissions(meeting, schedule, request.user) + cansee,canedit,secretariat = agenda_permissions(meeting, schedule, request.user) #read_only = not canedit ## not used - def is_truthy_enough(value): - return not (value == "0" or value == 0 or value=="false") - # TODO: Secretariat should always get canedit - if not (canedit or has_role(user, "Secretariat")): + if not (canedit or secretariat): return HttpResponse({'error':'no permission'}, status=403) - + if "public" in request.POST: schedule.public = is_truthy_enough(request.POST["public"]) @@ -355,7 +313,7 @@ def agenda_update(request, meeting, schedule): return HttpResponse(json.dumps(schedule.json_dict(request.build_absolute_uri('/'))), content_type="application/json") else: - return redirect(edit_agenda, meeting.number, schedule.name) + return redirect(edit_agenda, meeting.number, schedule.owner_email(), schedule.name) @role_required('Secretariat') def agenda_del(request, meeting, schedule): @@ -378,11 +336,13 @@ def agenda_infosurl(request, num=None): # unacceptable action return HttpResponse(status=406) -def agenda_infourl(request, num=None, name=None): - meeting = get_meeting(num) - #log.debug("agenda: %s / %s" % (meeting, name)) +def agenda_infourl(request, num=None, owner=None, name=None): + meeting = get_meeting(num) + person = get_person_by_email(owner) + schedule = get_schedule_by_name(meeting, person, name) + if schedule is None: + raise Http404("No meeting information for meeting %s schedule %s available" % (num,name)) - schedule = get_schedule(meeting, name) #debug.log("results in agenda: %u / %s" % (schedule.id, request.method)) if request.method == 'GET': @@ -469,8 +429,9 @@ def sessions_json(request, num): ## Scheduledsesion ############################################################################# +# this creates an entirely *NEW* scheduledsession def scheduledsessions_post(request, meeting, schedule): - cansee,canedit = agenda_permissions(meeting, schedule, request.user) + cansee,canedit,secretariat = agenda_permissions(meeting, schedule, request.user) if not canedit: return HttpResponse(json.dumps({'error':'no permission to modify this agenda'}), status = 403, @@ -495,7 +456,6 @@ def scheduledsessions_post(request, meeting, schedule): return HttpResponse(json.dumps({'error':'invalid extendedfrom value: %u' % val}), status = 406, content_type="application/json") - ss1.save() ss1_dict = ss1.json_dict(request.build_absolute_uri('/')) response = HttpResponse(json.dumps(ss1_dict), @@ -513,9 +473,8 @@ def scheduledsessions_get(request, num, schedule): content_type="application/json") # this returns the list of scheduled sessions for the given named agenda -def scheduledsessions_json(request, num, name): - meeting = get_meeting(num) - schedule = get_schedule(meeting, name) +def scheduledsessions_json(request, num, owner, name): + meeting, person, schedule = get_meeting_schedule(num, owner, name) if request.method == 'GET': return scheduledsessions_get(request, meeting, schedule) @@ -526,28 +485,38 @@ def scheduledsessions_json(request, num, name): status = 406, content_type="application/json") - -def scheduledsession_update(request, meeting, schedule, scheduledsession_id): - cansee,canedit = agenda_permissions(meeting, schedule, request.user) - if not canedit or True: - return HttpResponse(json.dumps({'error':'no permission to update this agenda'}), - status = 403, - content_type="application/json") - - -def scheduledsession_delete(request, meeting, schedule, scheduledsession_id): - cansee,canedit = agenda_permissions(meeting, schedule, request.user) +# accepts both POST and PUT in order to implement Postel Doctrine. +def scheduledsession_update(request, meeting, schedule, ss): + cansee,canedit,secretariat = agenda_permissions(meeting, schedule, request.user) if not canedit: return HttpResponse(json.dumps({'error':'no permission to update this agenda'}), status = 403, content_type="application/json") - scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id) + if request.method == 'POST': + put_vars = request.POST + ss.pinned = is_truthy_enough(put_vars["pinned"]) + else: + put_vars = QueryDict(request.body) + ss.pinned = is_truthy_enough(put_vars.get("pinned")) + + ss.save() + return HttpResponse(json.dumps({'message':'valid'}), + content_type="application/json") + +def scheduledsession_delete(request, meeting, schedule, ss): + cansee,canedit,secretariat = agenda_permissions(meeting, schedule, request.user) + if not canedit: + return HttpResponse(json.dumps({'error':'no permission to update this agenda'}), + status = 403, + content_type="application/json") + + # in case there is, somehow, more than one item with the same pk.. XXX + scheduledsessions = schedule.scheduledsession_set.filter(pk = ss.pk) if len(scheduledsessions) == 0: return HttpResponse(json.dumps({'error':'no such object'}), status = 404, content_type="application/json") - count=0 for ss in scheduledsessions: ss.delete() @@ -557,37 +526,40 @@ def scheduledsession_delete(request, meeting, schedule, scheduledsession_id): status = 200, content_type="application/json") -def scheduledsession_get(request, meeting, schedule, scheduledsession_id): - cansee,canedit = agenda_permissions(meeting, schedule, request.user) +def scheduledsession_get(request, meeting, schedule, ss): + cansee,canedit,secretariat = agenda_permissions(meeting, schedule, request.user) if not cansee: return HttpResponse(json.dumps({'error':'no permission to see this agenda'}), status = 403, content_type="application/json") - scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id) - if len(scheduledsessions) == 0: - return HttpResponse(json.dumps({'error':'no such object'}), - status = 404, - content_type="application/json") - - sess1_dict = scheduledsessions[0].json_dict(request.build_absolute_uri('/')) + sess1_dict = ss.json_dict(request.build_absolute_uri('/')) return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), content_type="application/json") -# this returns the list of scheduled sessions for the given named agenda -def scheduledsession_json(request, num, name, scheduledsession_id): - meeting = get_meeting(num) - schedule = get_schedule(meeting, name) +# this return a specific session, updates a session or deletes a SPECIFIC scheduled session +def scheduledsession_json(request, num, owner, name, scheduledsession_id): + meeting, person, schedule = get_meeting_schedule(num, owner, name) - scheduledsession_id = int(scheduledsession_id) + scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id) + if len(scheduledsessions) == 0: + return HttpResponse(json.dumps({'error' : 'invalid scheduledsession'}), + content_type="application/json", + status=404); + ss = scheduledsessions[0] if request.method == 'GET': - return scheduledsession_get(request, meeting, schedule, scheduledsession_id) - elif request.method == 'PUT': - return scheduledsession_update(request, meeting, schedule, scheduledsession_id) + return scheduledsession_get(request, meeting, schedule, ss) + elif request.method == 'PUT' or request.method=='POST': + return scheduledsession_update(request, meeting, schedule, ss) elif request.method == 'DELETE': - return scheduledsession_delete(request, meeting, schedule, scheduledsession_id) + return scheduledsession_delete(request, meeting, schedule, ss) + +############################################################################# +## Constraints API +############################################################################# + # Would like to cache for 1 day, but there are invalidation issues. #@cache_page(86400) diff --git a/ietf/meeting/helpers.py b/ietf/meeting/helpers.py index 6f9bde626..12e82b857 100644 --- a/ietf/meeting/helpers.py +++ b/ietf/meeting/helpers.py @@ -14,6 +14,7 @@ import debug # pyflakes:ignore from ietf.group.models import Group from ietf.ietfauth.utils import has_role, user_is_person +from ietf.person.models import Person from ietf.meeting.models import Meeting from ietf.utils.history import find_history_active_at @@ -125,6 +126,19 @@ def get_schedule_by_id(meeting, schedid): schedule = get_object_or_404(meeting.schedule_set, id=int(schedid)) return schedule +# seems this belongs in ietf/person/utils.py? +def get_person_by_email(email): + # email == None may actually match people who haven't set an email! + if email is None: + return None + return Person.objects.filter(email__address=email).distinct().first() + +def get_schedule_by_name(meeting, owner, name): + if owner is not None: + return meeting.schedule_set.filter(owner = owner, name = name).first() + else: + return meeting.schedule_set.filter(name = name).first() + def meeting_updated(meeting): meeting_time = datetime.datetime(*(meeting.date.timetuple()[:7])) ts = max(meeting.timeslot_set.aggregate(Max('modified'))["modified__max"] or meeting_time, @@ -137,12 +151,14 @@ def agenda_permissions(meeting, schedule, user): # do this in positive logic. cansee = False canedit = False + secretariat = False - if schedule.public: + if has_role(user, 'Secretariat'): cansee = True - elif has_role(user, 'Secretariat'): + secretariat = True + # NOTE: secretariat is not superuser for edit! + elif schedule.public: cansee = True - # secretariat is not superuser for edit! elif schedule.visible and has_role(user, ['Area Director', 'IAB Chair', 'IRTF Chair']): cansee = True @@ -150,7 +166,7 @@ def agenda_permissions(meeting, schedule, user): cansee = True canedit = True - return cansee, canedit + return cansee, canedit, secretariat def session_constraint_expire(session): from django.core.urlresolvers import reverse diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 02300c04b..39ca10af3 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -456,7 +456,7 @@ class Schedule(models.Model): return u"%s:%s(%s)" % (self.meeting, self.name, self.owner) def base_url(self): - return "/meeting/%s/agenda/%s" % (self.meeting.number, self.name) + return "/meeting/%s/agenda/%s/%s" % (self.meeting.number, self.owner_email(), self.name) # temporary property to pacify the places where Schedule.scheduledsession_set is used @property @@ -674,8 +674,9 @@ class ScheduledSession(models.Model): return "" def json_url(self): - return "/meeting/%s/schedule/%s/session/%u.json" % (self.schedule.meeting.number, - self.schedule.name, self.id) + return "/meeting/%s/agenda/%s/%s/session/%u.json" % (self.schedule.meeting.number, + self.schedule.owner_email(), + self.schedule.name, self.id) def json_dict(self, host_scheme): ss = dict() diff --git a/ietf/meeting/tests_api.py b/ietf/meeting/tests_api.py index 8e8fb615e..489fa8a90 100644 --- a/ietf/meeting/tests_api.py +++ b/ietf/meeting/tests_api.py @@ -13,13 +13,6 @@ from ietf.utils.mail import outbox class ApiTests(TestCase): - def test_dajaxice_core_js(self): - # this is vital for Dajaxice to work and we have hacked it - # slightly to avoid copying static files around, so make sure - # we can fetch it - r = self.client.get("/dajaxice/dajaxice.core.js") - self.assertEqual(r.status_code, 200) - def test_update_agenda(self): meeting = make_meeting_test_data() schedule = Schedule.objects.get(meeting__number=42,name="test-agenda") @@ -35,6 +28,7 @@ class ApiTests(TestCase): def do_unschedule(scheduledsession): url = urlreverse("ietf.meeting.ajax.scheduledsession_json", kwargs=dict(num=scheduledsession.session.meeting.number, + owner=scheduledsession.schedule.owner_email(), name=scheduledsession.schedule.name, scheduledsession_id=scheduledsession.pk,)) return self.client.delete(url) @@ -42,6 +36,7 @@ class ApiTests(TestCase): def do_schedule(schedule,session,timeslot): url = urlreverse("ietf.meeting.ajax.scheduledsessions_json", kwargs=dict(num=session.meeting.number, + owner=schedule.owner_email(), name=schedule.name,)) post_data = '{ "session_id": "%s", "timeslot_id": "%s" }'%(session.pk,timeslot.pk) return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded') @@ -50,6 +45,7 @@ class ApiTests(TestCase): session = scheduledsession.session url = urlreverse("ietf.meeting.ajax.scheduledsessions_json", kwargs=dict(num=session.meeting.number, + owner=schedule.owner_email(), name=schedule.name,)) post_data = '{ "session_id": "%s", "timeslot_id": "%s", "extendedfrom_id": "%s" }'%(session.pk,scheduledsession.timeslot.slot_to_the_right.pk,scheduledsession.pk) @@ -208,7 +204,8 @@ class ApiTests(TestCase): self.assertEqual(set([x['short_name'] for x in info]),set(['mars','ames'])) schedule = meeting.agenda - url = urlreverse("ietf.meeting.ajax.scheduledsessions_json",kwargs=dict(num=meeting.number,name=schedule.name)) + url = urlreverse("ietf.meeting.ajax.scheduledsessions_json", + kwargs=dict(num=meeting.number,owner=schedule.owner_email(),name=schedule.name)) r = self.client.get(url) self.assertEqual(r.status_code, 200) info = json.loads(r.content) @@ -249,7 +246,7 @@ class ApiTests(TestCase): # create slot self.client.login(username="secretary", password="secretary+password") r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 302) + self.assertEqual(r.status_code, 201) self.assertTrue(meeting.timeslot_set.filter(time=slot_time)) self.assertEqual(meeting.timeslot_set.count(),prior_slotcount+1) @@ -274,7 +271,9 @@ class ApiTests(TestCase): meeting = make_meeting_test_data() url = urlreverse("ietf.meeting.ajax.agenda_infourl", - kwargs=dict(num=meeting.number, name=meeting.agenda.name)) + kwargs=dict(num=meeting.number, + owner=meeting.agenda.owner_email(), + name=meeting.agenda.name)) r = self.client.get(url) info = json.loads(r.content) @@ -308,6 +307,7 @@ class ApiTests(TestCase): url = urlreverse("ietf.meeting.ajax.agenda_infourl", kwargs=dict(num=meeting.number, + owner=meeting.agenda.owner_email(), name=meeting.agenda.name)) post_data = { @@ -336,6 +336,7 @@ class ApiTests(TestCase): url = urlreverse("ietf.meeting.ajax.agenda_infourl", kwargs=dict(num=meeting.number, + owner=meeting.agenda.owner_email(), name=meeting.agenda.name)) # unauthorized delete self.client.login(username="plain", password="plain+password") @@ -393,115 +394,84 @@ class ApiTests(TestCase): def test_read_only(self): meeting = make_meeting_test_data() - data = { - 'argv': json.dumps({ - "meeting_num": meeting.number, - "schedule_id": meeting.agenda.pk, - })} - # Secretariat self.client.login(username="secretary", password="secretary+password") - r = self.client.post('/dajaxice/ietf.meeting.readonly/', data) + url = '/meeting/%s/agenda/%s/%s/permissions' % (meeting.number, meeting.agenda.owner.email_address(), meeting.agenda.name); + r = self.client.get(url) self.assertEqual(r.status_code, 200) info = json.loads(r.content) self.assertEqual(info['secretariat'], True) self.assertEqual(urlsplit(info['owner_href'])[2], "/person/%s.json" % meeting.agenda.owner_id) self.assertEqual(info['read_only'], True) - self.assertEqual(info['write_perm'], True) + self.assertEqual(info['save_perm'], True) # owner self.client.login(username=meeting.agenda.owner.user.username, password=meeting.agenda.owner.user.username+"+password") - r = self.client.post('/dajaxice/ietf.meeting.readonly/', data) + url = '/meeting/%s/agenda/%s/%s/permissions' % (meeting.number, meeting.agenda.owner.email_address(), meeting.agenda.name); + r = self.client.get(url) self.assertEqual(r.status_code, 200) info = json.loads(r.content) self.assertEqual(info['secretariat'], False) self.assertEqual(info['read_only'], False) - self.assertEqual(info['write_perm'], False) + self.assertEqual(info['save_perm'], False) def test_update_timeslot_pinned(self): meeting = make_meeting_test_data() scheduled = ScheduledSession.objects.filter( session__meeting=meeting, session__group__acronym="mars").first() - url = '/dajaxice/ietf.meeting.update_timeslot_pinned/' + url = '/meeting/%s/agenda/%s/%s/session/%u.json' % (meeting.number, meeting.agenda.owner_email(), meeting.agenda.name, scheduled.pk) post_data = { - 'argv': json.dumps({ - "schedule_id": meeting.agenda.pk, - "scheduledsession_id": scheduled.pk, - "pinned": True, - })} + "pinned": True + } - # unauthorized post - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 200) - self.assertTrue("error" in json.loads(r.content)) + # unauthorized post gets failure (no redirect) + r = self.client.put(url, post_data) + self.assertEqual(r.status_code, 403, + "post to %s should have failed, no permission, got: %u/%s" % + (url, r.status_code, r.content)) self.assertTrue(not ScheduledSession.objects.get(pk=scheduled.pk).pinned) # set pinned meeting.agenda.owner = Person.objects.get(user__username="secretary") meeting.agenda.save() + + # need to rebuild URL, since the agenda owner has changed. + url = '/meeting/%s/agenda/%s/%s/session/%u.json' % (meeting.number, meeting.agenda.owner_email(), meeting.agenda.name, scheduled.pk) + self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 200) + r = self.client.put(url, post_data) + self.assertEqual(r.status_code, 200, + "post to %s should have worked, but got: %u/%s" % + (url, r.status_code, r.content)) self.assertTrue(ScheduledSession.objects.get(pk=scheduled.pk).pinned) -class UnusedButExposedApiTests(TestCase): +class TimeSlotEditingApiTests(TestCase): - def test_manipulate_timeslot_via_dajaxice(self): + def test_manipulate_timeslot(self): meeting = make_meeting_test_data() - slot_time = datetime.date.today() + slot = meeting.timeslot_set.all()[0] + self.assertEqual(TimeSlot.objects.get(pk=slot.pk).type.name,'Session') - url = '/dajaxice/ietf.meeting.update_timeslot_purpose/' - - create_post_data = { - 'argv' : json.dumps({ - "meeting_num" : meeting.number, - "timeslot_id" : 0, - "purpose" : "plenary", - "room_id" : meeting.room_set.first().id, - "time" : slot_time.strftime("%Y-%m-%d %H:%M:%S"), - "duration" : 3600 - })} - - prior_timeslot_count = meeting.timeslot_set.count() - # Create as nobody should fail - r = self.client.post(url, create_post_data) - self.assertEqual(r.status_code, 200) - info = json.loads(r.content) - self.assertTrue('error' in info and info['error']=='no permission') - self.assertEqual(meeting.timeslot_set.count(),prior_timeslot_count) - - # Successful create - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, create_post_data) - self.assertEqual(r.status_code, 200) - info = json.loads(r.content) - self.assertFalse('error' in info) - self.assertTrue('roomtype' in info) - self.assertEqual(info['roomtype'],'plenary') - self.assertEqual(meeting.timeslot_set.count(),prior_timeslot_count+1) + url = urlreverse("ietf.meeting.ajax.timeslot_sloturl", + kwargs=dict(num=meeting.number, slotid=slot.pk)) modify_post_data = { - 'argv' : json.dumps({ - "meeting_num" : meeting.number, - "timeslot_id" : meeting.timeslot_set.get(time=slot_time).id, - "purpose" : "session" - })} + "purpose" : "plenary" + } # Fail as non-secretariat self.client.login(username="plain", password="plain+password") r = self.client.post(url, modify_post_data) - self.assertEqual(r.status_code, 200) - info = json.loads(r.content) - self.assertTrue('error' in info and info['error']=='no permission') - self.assertEqual(meeting.timeslot_set.get(time=slot_time).type.name,'Plenary') + self.assertEqual(r.status_code, 403) + self.assertEqual(TimeSlot.objects.get(pk=slot.pk).type.name,'Session') # Successful change of purpose self.client.login(username="secretary", password="secretary+password") r = self.client.post(url, modify_post_data) self.assertEqual(r.status_code, 200) - self.assertEqual(meeting.timeslot_set.get(time=slot_time).type.name,'Session') + self.assertEqual(TimeSlot.objects.get(pk=slot.pk).type.name,'Plenary') diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py index a61aa17ca..bec88951d 100644 --- a/ietf/meeting/tests_js.py +++ b/ietf/meeting/tests_js.py @@ -55,7 +55,7 @@ class ScheduleEditTests(LiveServerTestCase): self.assertEqual(ScheduledSession.objects.filter(session__meeting__number=42,session__group__acronym='mars').count(),1) self.login() - url = self.absreverse('ietf.meeting.views.edit_agenda',kwargs=dict(num='42',name='test-agenda')) + url = self.absreverse('ietf.meeting.views.edit_agenda',kwargs=dict(num='42',name='test-agenda',owner='plain@example.com')) self.driver.get(url) q = PyQuery(self.driver.page_source) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 07e59c171..0101287a1 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -163,12 +163,16 @@ class EditTests(TestCase): meeting = make_meeting_test_data() # try to get non-existing agenda - url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, name="foo")) + url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, + owner=meeting.agenda.owner_email(), + name="foo")) r = self.client.get(url) self.assertEqual(r.status_code, 404) - # save as - url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number)) + # save as new name (requires valid existing agenda) + url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, + owner=meeting.agenda.owner_email(), + name=meeting.agenda.name)) self.client.login(username="ad", password="ad+password") r = self.client.post(url, { 'savename': "foo", @@ -177,11 +181,13 @@ class EditTests(TestCase): self.assertEqual(r.status_code, 302) # get - url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, name="foo")) + schedule = meeting.get_schedule_by_name("foo") + url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, + owner=schedule.owner_email(), + name="foo")) r = self.client.get(url) self.assertEqual(r.status_code, 200) - schedule = meeting.get_schedule_by_name("foo") schedule.visible = True schedule.public = False schedule.save() diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index e55d8bd8d..a4443f1fe 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -20,14 +20,16 @@ urlpatterns = patterns('', (r'^agenda.ics$', views.ical_agenda), (r'^agenda/week-view.html$', views.week_view), (r'^week-view.html$', views.week_view), - (r'^(?P\d+)/schedule/edit$', views.edit_agenda), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/edit$', views.edit_agenda), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)(?P.html)?/?$', views.agenda), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+)/edit$', views.edit_agenda), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+).(?P.html)?/?$', views.agenda), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+)/permissions$', ajax.agenda_permission_api), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+)/session/(?P\d+).json$', ajax.scheduledsession_json), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+)/sessions.json$', ajax.scheduledsessions_json), + (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl), + (r'^(?P\d+)/agenda/edit$', views.edit_agenda), (r'^(?P\d+)/agenda(?P.html)?/?$', views.agenda), (r'^(?P\d+)/(?Pagenda-utc)(?P.html)?/?$', views.agenda), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/sessions.json$', ajax.scheduledsessions_json), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/session/(?P\d+).json$', ajax.scheduledsession_json), (r'^(?P\d+)/requests.html$', RedirectView.as_view(url='/meeting/%(num)s/requests', permanent=True)), (r'^(?P\d+)/requests$', views.meeting_requests), (r'^(?P\d+)/agenda(?P.txt)$', views.agenda), @@ -41,9 +43,8 @@ urlpatterns = patterns('', (r'^(?P\d+)/timeslots$', ajax.timeslot_slotsurl), (r'^(?P\d+)/timeslots.json$', ajax.timeslot_slotsurl), (r'^(?P\d+)/timeslot/(?P\d+).json$', ajax.timeslot_sloturl), - (r'^(?P\d+)/agendas/(?P[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl), - (r'^(?P\d+)/agendas$', ajax.agenda_infosurl), - (r'^(?P\d+)/agendas.json$', ajax.agenda_infosurl), + (r'^(?P\d+)/agendas$', ajax.agenda_infosurl), + (r'^(?P\d+)/agendas.json$', ajax.agenda_infosurl), (r'^(?P\d+)/week-view.html$', views.week_view), (r'^(?P\d+)/agenda/week-view.html$', views.week_view), (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-]+)-drafts.pdf$', views.session_draft_pdf), diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 6a34808e4..32362dc3f 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -26,7 +26,7 @@ from ietf.doc.models import Document, State from ietf.group.models import Group from ietf.ietfauth.utils import role_required, has_role from ietf.meeting.models import Meeting, TimeSlot, Session, Schedule, Room -from ietf.meeting.helpers import get_areas +from ietf.meeting.helpers import get_areas, get_person_by_email, get_schedule_by_name from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list from ietf.meeting.helpers import get_all_scheduledsessions_from_schedule from ietf.meeting.helpers import get_modified_from_scheduledsessions @@ -92,13 +92,14 @@ class SaveAsForm(forms.Form): savename = forms.CharField(max_length=100) @role_required('Area Director','Secretariat') -def agenda_create(request, num=None, name=None): - meeting = get_meeting(num) - schedule = get_schedule(meeting, name) +def agenda_create(request, num=None, owner=None, name=None): + meeting = get_meeting(num) + person = get_person_by_email(owner) + schedule = get_schedule_by_name(meeting, person, name) if schedule is None: # here we have to return some ajax to display an error. - raise Http404("No meeting information for meeting %s schedule %s available" % (num,name)) + raise Http404("No meeting information for meeting %s owner %s schedule %s available" % (num, owner, name)) # authorization was enforced by the @group_require decorator above. @@ -152,7 +153,7 @@ def agenda_create(request, num=None, name=None): # now redirect to this new schedule. - return redirect(edit_agenda, meeting.number, newschedule.name) + return redirect(edit_agenda, meeting.number, newschedule.owner_email(), newschedule.name) @role_required('Secretariat') @@ -225,14 +226,20 @@ def edit_roomurl(request, num, roomid): #@role_required('Area Director','Secretariat') # disable the above security for now, check it below. @ensure_csrf_cookie -def edit_agenda(request, num=None, name=None): +def edit_agenda(request, num=None, owner=None, name=None): if request.method == 'POST': - return agenda_create(request, num, name) + return agenda_create(request, num, owner, name) - user = request.user - meeting = get_meeting(num) - schedule = get_schedule(meeting, name) + user = request.user + meeting = get_meeting(num) + person = get_person_by_email(owner) + if name is None: + schedule = meeting.agenda + else: + schedule = get_schedule_by_name(meeting, person, name) + if schedule is None: + raise Http404("No meeting information for meeting %s owner %s schedule %s available" % (num, owner, name)) meeting_base_url = request.build_absolute_uri(meeting.base_url()) site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash @@ -241,9 +248,9 @@ def edit_agenda(request, num=None, name=None): rooms = rooms.all() saveas = SaveAsForm() saveasurl=reverse(edit_agenda, - args=[meeting.number, schedule.name]) + args=[meeting.number, schedule.owner_email(), schedule.name]) - can_see, can_edit = agenda_permissions(meeting, schedule, user) + can_see, can_edit,secretariat = agenda_permissions(meeting, schedule, user) if not can_see: return HttpResponse(render_to_string("meeting/private_agenda.html", @@ -294,13 +301,15 @@ AgendaPropertiesForm = modelform_factory(Schedule, fields=('name','visible', 'pu @role_required('Area Director','Secretariat') @ensure_csrf_cookie -def edit_agenda_properties(request, num=None, name=None): - - meeting = get_meeting(num) - schedule = get_schedule(meeting, name) +def edit_agenda_properties(request, num=None, owner=None, name=None): + meeting = get_meeting(num) + person = get_person_by_email(owner) + schedule = get_schedule_by_name(meeting, person, name) + if schedule is None: + raise Http404("No meeting information for meeting %s owner %s schedule %s available" % (num, owner, name)) form = AgendaPropertiesForm(instance=schedule) - cansee, canedit = agenda_permissions(meeting, schedule, request.user) + cansee, canedit, secretariat = agenda_permissions(meeting, schedule, request.user) if not (canedit or has_role(request.user,'Secretariat')): return HttpResponseForbidden("You may not edit this agenda") @@ -320,7 +329,7 @@ def edit_agenda_properties(request, num=None, name=None): def edit_agendas(request, num=None, order=None): #if request.method == 'POST': - # return agenda_create(request, num, name) + # return agenda_create(request, num, owner, name) meeting = get_meeting(num) user = request.user diff --git a/ietf/person/migrations/0001_change_system_email.py b/ietf/person/migrations/0001_change_system_email.py new file mode 100644 index 000000000..e95a4dbe7 --- /dev/null +++ b/ietf/person/migrations/0001_change_system_email.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + orm.Email.objects.filter(person__name='(System)').update(address='system@datatracker.ietf.org') + + def backwards(self, orm): + orm.Email.objects.filter(person__name='(System)').update(address='(System)') + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'person.alias': { + 'Meta': {'object_name': 'Alias'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}) + }, + u'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + u'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + u'person.personhistory': { + 'Meta': {'object_name': 'PersonHistory'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': u"orm['person.Person']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['person'] + symmetrical = True diff --git a/dajaxice/templatetags/__init__.py b/ietf/person/migrations/__init__.py similarity index 100% rename from dajaxice/templatetags/__init__.py rename to ietf/person/migrations/__init__.py diff --git a/ietf/settings.py b/ietf/settings.py index bf7ffe704..1d5d55b3a 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -94,8 +94,6 @@ STATIC_ROOT = os.path.abspath(BASE_DIR + "/../static/") WSGI_APPLICATION = "ietf.wsgi.application" -DAJAXICE_MEDIA_PREFIX = "dajaxice" - AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', ) #DATABASE_ROUTERS = ["ietf.legacy_router.LegacyRouter"] @@ -175,7 +173,6 @@ ROOT_URLCONF = 'ietf.urls' TEMPLATE_DIRS = ( BASE_DIR + "/templates", BASE_DIR + "/secr/templates", - BASE_DIR+"/../django-dajaxice/dajaxice/templates", ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -236,7 +233,6 @@ INSTALLED_APPS = ( 'ietf.secr.sreq', 'ietf.nomcom', 'ietf.dbtemplate', - 'dajaxice', ) INTERNAL_IPS = ( diff --git a/ietf/templates/meeting/agenda_list.html b/ietf/templates/meeting/agenda_list.html index 6b5d4f89c..f7f8f137b 100644 --- a/ietf/templates/meeting/agenda_list.html +++ b/ietf/templates/meeting/agenda_list.html @@ -3,8 +3,6 @@ {# Copyright The IETF Trust 2007, All Rights Reserved #} {% load humanize %} -{% load dajaxice_templatetags %} - {% block title %}IETF {{ meeting.number }} Meeting Agenda{% endblock %} {% load agenda_custom_tags %} {% block pagehead %} @@ -21,8 +19,6 @@ -{% dajaxice_js_import %} - @@ -61,11 +57,11 @@ {{ agenda.official_token }} {{ agenda.owner }} - {{ agenda.name }} + {{ agenda.name }} {{ agenda.visible_token }} {{ agenda.public_token }} - EDIT + EDIT DEL {% endfor %} diff --git a/ietf/templates/meeting/landscape_edit.html b/ietf/templates/meeting/landscape_edit.html index ffd401329..0e5f5bfac 100644 --- a/ietf/templates/meeting/landscape_edit.html +++ b/ietf/templates/meeting/landscape_edit.html @@ -3,8 +3,6 @@ {# Copyright The IETF Trust 2007, All Rights Reserved #} {% load humanize %} -{% load dajaxice_templatetags %} - {% block morecss %} {% for area in area_list %} .{{ area.upcase_acronym}}-scheme, .meeting_event th.{{ area.upcase_acronym}}-scheme, #{{ area.upcase_acronym }}-groups, #selector-{{ area.upcase_acronym }} { color:{{ area.fg_color }}; background-color: {{ area.bg_color }} } @@ -31,8 +29,6 @@ -{% dajaxice_js_import %} - @@ -48,8 +44,9 @@ var schedule_id = {{ schedule.id }}; var schedule_owner_href = "{{ schedule.owner_email }}"; var schedule_name = "{{ schedule.name }}"; var meeting_base_url = "{{ meeting_base_url }}"; +var schedule_owner_email = "{{ schedule.owner_email }}"; var site_base_url = "{{ site_base_url }}"; -var scheduledsession_post_href = "{% url "ietf.meeting.ajax.scheduledsessions_json" meeting.number schedule.name %}"; +var scheduledsession_post_href = "{% url "ietf.meeting.ajax.scheduledsessions_json" meeting.number schedule.owner_email schedule.name %}"; var total_days = {{time_slices|length}}; var total_rooms = {{rooms|length}}; diff --git a/ietf/templates/meeting/private_agenda.html b/ietf/templates/meeting/private_agenda.html index 8deacec2b..ebed2faf1 100644 --- a/ietf/templates/meeting/private_agenda.html +++ b/ietf/templates/meeting/private_agenda.html @@ -3,8 +3,6 @@ {# Copyright The IETF Trust 2007, All Rights Reserved #} {% load humanize %} -{% load dajaxice_templatetags %} - {% block morecss %} {% for area in area_list %} .{{ area.upcase_acronym}}-scheme, .meeting_event th.{{ area.upcase_acronym}}-scheme, #{{ area.upcase_acronym }}-groups, #selector-{{ area.upcase_acronym }} { color:{{ area.fg_color }}; background-color: {{ area.bg_color }} } diff --git a/ietf/templates/meeting/properties_edit.html b/ietf/templates/meeting/properties_edit.html index 802d91731..6e5108825 100644 --- a/ietf/templates/meeting/properties_edit.html +++ b/ietf/templates/meeting/properties_edit.html @@ -3,8 +3,6 @@ {# Copyright The IETF Trust 2007, All Rights Reserved #} {% load humanize %} -{% load dajaxice_templatetags %} - {% block title %}IETF {{ meeting.number }} Meeting Agenda: {{schedule.owner}} / {{ schedule.name }}{% endblock %} {% load agenda_custom_tags %} {% block pagehead %} @@ -21,8 +19,6 @@ -{% dajaxice_js_import %} - diff --git a/ietf/templates/meeting/room_edit.html b/ietf/templates/meeting/room_edit.html index c63a7a478..9751cf2c5 100644 --- a/ietf/templates/meeting/room_edit.html +++ b/ietf/templates/meeting/room_edit.html @@ -3,8 +3,6 @@ {# Copyright The IETF Trust 2007, All Rights Reserved #} {% load humanize %} -{% load dajaxice_templatetags %} - {% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %} {% load agenda_custom_tags %} {% block pagehead %} @@ -30,8 +28,6 @@ -{% dajaxice_js_import %} - {% endblock js %} diff --git a/ietf/templates/meeting/timeslot_edit.html b/ietf/templates/meeting/timeslot_edit.html index 88cbc4457..d39d61c2c 100644 --- a/ietf/templates/meeting/timeslot_edit.html +++ b/ietf/templates/meeting/timeslot_edit.html @@ -3,8 +3,6 @@ {# Copyright The IETF Trust 2007, All Rights Reserved #} {% load humanize %} -{% load dajaxice_templatetags %} - {% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %} {% load agenda_custom_tags %} {% block pagehead %} @@ -30,8 +28,6 @@ -{% dajaxice_js_import %} - @@ -41,21 +37,17 @@ - -