From d7f20342b6d0ce7a6eaacffd72e2b495964308b2 Mon Sep 17 00:00:00 2001
From: Jennifer Richards <jennifer@painless-security.com>
Date: Thu, 4 Nov 2021 17:07:22 +0000
Subject: [PATCH] Tear out old meeting schedule editor and related code  -
 Legacy-Id: 19551

---
 ietf/group/models.py                          |   28 -
 ietf/group/urls.py                            |    1 -
 ietf/group/views.py                           |    8 -
 ietf/meeting/ajax.py                          |  629 ------
 ietf/meeting/helpers.py                       |  100 +-
 ietf/meeting/models.py                        |  228 +-
 ietf/meeting/tests_api.py                     |  501 -----
 ietf/meeting/tests_js.py                      |    4 +-
 ietf/meeting/tests_views.py                   |  133 +-
 ietf/meeting/urls.py                          |   25 +-
 ietf/meeting/views.py                         |  162 +-
 ietf/person/ajax.py                           |    8 -
 ietf/person/models.py                         |   12 -
 ietf/person/urls.py                           |    1 -
 ietf/secr/sreq/views.py                       |    4 -
 ietf/static/ietf/js/agenda/agenda_edit.js     |  184 --
 ietf/static/ietf/js/agenda/agenda_helpers.js  |  626 ------
 .../static/ietf/js/agenda/agenda_listeners.js | 1266 -----------
 ietf/static/ietf/js/agenda/agenda_objects.js  | 1935 -----------------
 .../meeting/edit_meeting_schedule.html        |    2 +-
 ietf/templates/meeting/landscape_edit.html    |  388 ----
 ietf/templates/meeting/schedule_list.html     |    4 +-
 22 files changed, 88 insertions(+), 6161 deletions(-)
 delete mode 100644 ietf/meeting/ajax.py
 delete mode 100644 ietf/meeting/tests_api.py
 delete mode 100644 ietf/static/ietf/js/agenda/agenda_edit.js
 delete mode 100644 ietf/static/ietf/js/agenda/agenda_helpers.js
 delete mode 100644 ietf/static/ietf/js/agenda/agenda_listeners.js
 delete mode 100644 ietf/static/ietf/js/agenda/agenda_objects.js
 delete mode 100644 ietf/templates/meeting/landscape_edit.html

diff --git a/ietf/group/models.py b/ietf/group/models.py
index 8e2c55f9c..d97b50ab4 100644
--- a/ietf/group/models.py
+++ b/ietf/group/models.py
@@ -8,16 +8,12 @@ import jsonfield
 import os
 import re
 
-from urllib.parse import urljoin
-
 from django.conf import settings
 from django.core.validators import RegexValidator
 from django.db import models
 from django.db.models.deletion import CASCADE, PROTECT
 from django.dispatch import receiver
 
-#from simple_history.models import HistoricalRecords
-
 import debug                            # pyflakes:ignore
 
 from ietf.group.colors import fg_group_colors, bg_group_colors
@@ -168,30 +164,6 @@ class Group(GroupInfo):
     def bg_color(self):
         return bg_group_colors[self.upcase_acronym]
 
-    def json_url(self):
-        return "/group/%s.json" % (self.acronym,)
-
-    def json_dict(self, host_scheme):
-        group1= dict()
-        group1['href'] = urljoin(host_scheme, self.json_url())
-        group1['acronym'] = self.acronym
-        group1['name']    = self.name
-        group1['state']   = self.state.slug
-        group1['type']    = self.type.slug
-        if self.parent is not None:
-            group1['parent_href']  = urljoin(host_scheme, self.parent.json_url())
-        # uncomment when people URL handle is created
-        try:
-            if self.ad_role() is not None:
-                group1['ad_href']      = urljoin(host_scheme, self.ad_role().person.json_url())
-        except Person.DoesNotExist:
-            pass
-        group1['list_email'] = self.list_email
-        group1['list_subscribe'] = self.list_subscribe
-        group1['list_archive'] = self.list_archive
-        group1['comments']     = self.comments
-        return group1
-
     def has_tools_page(self):
         return self.type_id in ['wg', ] and self.state_id in ['active', 'dormant', 'replaced', 'conclude']
 
diff --git a/ietf/group/urls.py b/ietf/group/urls.py
index a8d6a06e6..07eb2c4b6 100644
--- a/ietf/group/urls.py
+++ b/ietf/group/urls.py
@@ -54,7 +54,6 @@ info_detail_urls = [
 group_urls = [
     url(r'^$', views.active_groups), 
     url(r'^groupmenu.json', views.group_menu_data, None, 'ietf.group.views.group_menu_data'),
-    url(r'^%(acronym)s.json$' % settings.URL_REGEXPS, views.group_json),
     url(r'^chartering/$', views.chartering_groups),
     url(r'^chartering/create/(?P<group_type>(wg|rg))/$', views.edit, {'action': "charter"}),
     url(r'^concluded/$', views.concluded_groups),
diff --git a/ietf/group/views.py b/ietf/group/views.py
index d41a925f3..fcb3318ae 100644
--- a/ietf/group/views.py
+++ b/ietf/group/views.py
@@ -38,7 +38,6 @@ import copy
 import datetime
 import itertools
 import io
-import json
 import markdown
 import math
 import os
@@ -1299,13 +1298,6 @@ def stream_edit(request, acronym):
                 )
 
 
-def group_json(request, acronym):
-    group = get_object_or_404(Group, acronym=acronym)
-
-    return HttpResponse(json.dumps(group.json_dict(request.build_absolute_uri('/')),
-                                   sort_keys=True, indent=2),
-                        content_type="application/json")
-
 @cache_control(public=True, max_age=30*60)
 @cache_page(30 * 60)
 def group_menu_data(request):
diff --git a/ietf/meeting/ajax.py b/ietf/meeting/ajax.py
deleted file mode 100644
index b8a3c01a5..000000000
--- a/ietf/meeting/ajax.py
+++ /dev/null
@@ -1,629 +0,0 @@
-# Copyright The IETF Trust 2013-2019, All Rights Reserved
-import json
-
-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 ietf.ietfauth.utils import role_required, has_role
-from ietf.meeting.helpers import get_meeting, get_schedule, schedule_permissions, get_person_by_email, get_schedule_by_name
-from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint, SchedTimeSessAssignment, ResourceAssociation
-from ietf.meeting.views import edit_timeslots, edit_meeting_schedule
-
-import debug                            # pyflakes:ignore
-
-def is_truthy_enough(value):
-    return not (value == "0" or value == 0 or value=="false")
-
-# 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 schedule is read-only require any kind of permission?
-def schedule_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
-    cansee      = False
-    canedit     = False
-    owner_href  = ""
-
-    if schedule is not None:
-        cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user)
-        owner_href = request.build_absolute_uri(schedule.owner.json_url())
-
-    if has_role(request.user, "Area Director") or secretariat:
-        save_perm  = True
-
-    return HttpResponse(json.dumps({'secretariat': secretariat,
-                                    'save_perm':   save_perm,
-                                    'read_only':   canedit==False,
-                                    'owner_href':  owner_href}),
-                        content_type="application/json")
-
-#############################################################################
-## ROOM API
-#############################################################################
-from django.forms.models import modelform_factory
-AddRoomForm = modelform_factory(Room, exclude=('meeting','time'))
-
-# no authorization required
-def timeslot_roomlist(request, mtg):
-    rooms = mtg.room_set.all()
-    json_array=[]
-    for room in rooms:
-        json_array.append(room.json_dict(request.build_absolute_uri('/')))
-    return HttpResponse(json.dumps(json_array),
-                        content_type="application/json")
-
-@role_required('Secretariat')
-def timeslot_addroom(request, meeting):
-    newroomform = AddRoomForm(request.POST)
-    if not newroomform.is_valid():
-        return HttpResponse(status=404)
-
-    newroom = newroomform.save(commit=False)
-    newroom.meeting = meeting
-    newroom.save()
-    newroom.create_timeslots()
-
-    if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']:
-        return redirect(timeslot_roomurl, meeting.number, newroom.pk)
-    else:
-        return redirect(edit_timeslots, meeting.number)
-
-@role_required('Secretariat')
-def timeslot_delroom(request, meeting, roomid):
-    room = get_object_or_404(meeting.room_set, pk=roomid)
-
-    room.delete_timeslots()
-    room.delete()
-    return HttpResponse('{"error":"none"}', status = 200)
-
-@role_required('Secretariat')
-def timeslot_updroom(request, meeting, roomid):
-    room = get_object_or_404(meeting.room_set, pk=roomid)
-
-    if "name" in request.POST:
-        room.name = request.POST["name"]
-
-    if "capacity" in request.POST:
-        room.capacity = request.POST["capacity"]
-
-    if "resources" in request.POST:
-        new_resource_ids = request.POST["resources"]
-        new_resources = [ ResourceAssociation.objects.get(pk=a)
-                          for a in new_resource_ids]
-        room.resources = new_resources
-
-    room.save()
-    return HttpResponse('{"error":"none"}', status = 200)
-
-def timeslot_roomsurl(request, num=None):
-    meeting = get_meeting(num)
-
-    if request.method == 'GET':
-        return timeslot_roomlist(request, meeting)
-    elif request.method == 'POST':
-        return timeslot_addroom(request, meeting)
-
-    # unacceptable reply
-    return HttpResponse(status=406)
-
-def timeslot_roomurl(request, num=None, roomid=None):
-    meeting = get_meeting(num)
-
-    if request.method == 'GET':
-        room = get_object_or_404(meeting.room_set, pk=roomid)
-        return HttpResponse(json.dumps(room.json_dict(request.build_absolute_uri('/'))),
-                            content_type="application/json")
-    elif request.method == 'PUT':
-        return timeslot_updroom(request, meeting, roomid)
-    elif request.method == 'DELETE':
-        return timeslot_delroom(request, meeting, roomid)
-
-#############################################################################
-## DAY/SLOT API
-##  -- this creates groups of timeslots, and associated schedtimesessassignments.
-#############################################################################
-AddSlotForm = modelform_factory(TimeSlot, exclude=('meeting','name','location','sessions', 'modified'))
-
-# no authorization required to list.
-def timeslot_slotlist(request, mtg):
-    slots = mtg.timeslot_set.all()
-    # Restrict graphical editing to slots of type 'regular' for now
-    slots = slots.filter(type__slug='regular')
-    json_array=[]
-    for slot in slots:
-        json_array.append(slot.json_dict(request.build_absolute_uri('/')))
-    return HttpResponse(json.dumps(json_array, sort_keys=True, indent=2),
-                        content_type="application/json")
-
-@role_required('Secretariat')
-def timeslot_addslot(request, meeting):
-    addslotform = AddSlotForm(request.POST)
-    #debug.log("newslot: %u" % ( addslotform.is_valid() ))
-    if not addslotform.is_valid():
-        return HttpResponse(status=404)
-
-    newslot = addslotform.save(commit=False)
-    newslot.meeting = meeting
-    newslot.save()
-
-    # XXX FIXME: timeslot_dayurl is undefined.  Placeholder:
-    # timeslot_dayurl = None
-    # XXX FIXME: newroom is undefined.  Placeholder:
-    # 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:
-        put_vars = QueryDict(request.body)
-        slot.type_id = put_vars.get("purpose")
-
-    slot.save()
-
-    # WORKAROUND: Right now, if there are sessions scheduled in this timeslot
-    # when it is marked unavailable (or any other value besides 'regular') they
-    # become unreachable from the editing screen. The session is listed in the
-    # "unscheduled" block incorrectly, and drag-dropping it onto the a new
-    # timeslot produces erroneous results. To avoid this, we will silently
-    # unschedule any sessions in the timeslot that has just been made
-    # unavailable.
-
-    if slot.type_id != 'regular':
-        slot.sessionassignments.all().delete()
-
-    # ENDWORKAROUND
-
-    # 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):
-    slot = get_object_or_404(meeting.timeslot_set, pk=slotid)
-
-    # this will delete self as well.
-    slot.delete_concurrent_timeslots()
-    return HttpResponse('{"error":"none"}', status = 200)
-
-def timeslot_slotsurl(request, num=None):
-    meeting = get_meeting(num)
-
-    if request.method == 'GET':
-        return timeslot_slotlist(request, meeting)
-    elif request.method == 'POST':
-        return timeslot_addslot(request, meeting)
-
-    # unacceptable reply
-    return HttpResponse(status=406)
-
-def timeslot_sloturl(request, num=None, slotid=None):
-    meeting = get_meeting(num)
-
-    if request.method == 'GET':
-        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' or request.method == 'PUT':
-        return timeslot_updslot(request, meeting, slotid)
-    elif request.method == 'DELETE':
-        return timeslot_delslot(request, meeting, slotid)
-
-#############################################################################
-## Schedule List API
-#############################################################################
-ScheduleEntryForm = modelform_factory(Schedule, exclude=('meeting','owner'))
-EditScheduleEntryForm = modelform_factory(Schedule, exclude=('meeting','owner', 'name'))
-
-@role_required('Area Director','Secretariat')
-def schedule_list(request, mtg):
-    schedules = mtg.schedule_set.all()
-    json_array=[]
-    for schedule in schedules:
-        json_array.append(schedule.json_dict(request.build_absolute_uri('/')))
-    return HttpResponse(json.dumps(json_array),
-                        content_type="application/json")
-
-# duplicates save-as functionality below.
-@role_required('Area Director','Secretariat')
-def schedule_add(request, meeting):
-    newscheduleform = ScheduleEntryForm(request.POST)
-    if not newscheduleform.is_valid():
-        return HttpResponse(status=404)
-
-    newschedule = newscheduleform.save(commit=False)
-    newschedule.meeting = meeting
-    newschedule.owner   = request.user.person
-    newschedule.save()
-
-    if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']:
-        return redirect(schedule_infourl, meeting.number, newschedule.owner_email(), newschedule.name)
-    else:
-        return redirect(edit_meeting_schedule, meeting.number, newschedule.owner_email(), newschedule.name)
-
-@require_POST
-def schedule_update(request, meeting, schedule):
-    # forms are completely useless for update actions that want to
-    # accept a subset of values. (huh? we could use required=False)
-
-    user = request.user
-
-    if not user.is_authenticated:
-        return HttpResponse({'error':'no permission'}, status=403)
-
-    cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user)
-    #read_only = not canedit ## not used
-
-    # TODO: Secretariat should always get canedit
-    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"])
-
-    if "visible" in request.POST:
-        schedule.visible = is_truthy_enough(request.POST["visible"])
-
-    if "name" in request.POST:
-        schedule.name = request.POST["name"]
-
-    schedule.save()
-
-    # enforce that a non-public schedule can not be the public one.
-    if meeting.schedule == schedule and not schedule.public:
-        meeting.schedule = None
-        meeting.save()
-
-    if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']:
-        return HttpResponse(json.dumps(schedule.json_dict(request.build_absolute_uri('/'))),
-                            content_type="application/json")
-    else:
-        return redirect(edit_meeting_schedule, meeting.number, schedule.owner_email(), schedule.name)
-
-@role_required('Secretariat')
-def schedule_del(request, meeting, schedule):
-    schedule.delete_assignments()
-    #debug.log("deleting meeting: %s schedule: %s" % (meeting, meeting.schedule))
-    if meeting.schedule == schedule:
-        meeting.schedule = None
-        meeting.save()
-    schedule.delete()
-    return HttpResponse('{"error":"none"}', status = 200)
-
-def schedule_infosurl(request, num=None):
-    meeting = get_meeting(num)
-
-    if request.method == 'GET':
-        return schedule_list(request, meeting)
-    elif request.method == 'POST':
-        return schedule_add(request, meeting)
-
-    # unacceptable action
-    return HttpResponse(status=406)
-
-def schedule_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))
-
-    #debug.log("results in schedule: %u / %s" % (schedule.id, request.method))
-
-    if request.method == 'GET':
-        return HttpResponse(json.dumps(schedule.json_dict(request.build_absolute_uri('/'))),
-                            content_type="application/json")
-    elif request.method == 'POST':
-        return schedule_update(request, meeting, schedule)
-    elif request.method == 'DELETE':
-        return schedule_del(request, meeting, schedule)
-    else:
-        return HttpResponse(status=406)
-
-#############################################################################
-## Meeting API  (very limited)
-#############################################################################
-
-def meeting_get(request, meeting):
-    return HttpResponse(json.dumps(meeting.json_dict(request.build_absolute_uri('/')),
-                                sort_keys=True, indent=2),
-                        content_type="application/json")
-
-@role_required('Secretariat')
-def meeting_update(request, meeting):
-    # at present, only the official schedule can be updated from this interface.
-
-    #debug.log("1 meeting.schedule: %s / %s / %s" % (meeting.schedule, update_dict, request.body))
-    if "schedule" in request.POST:
-        value = request.POST["schedule"]
-        #debug.log("4 meeting.schedule: %s" % (value))
-        if not value or value == "None": # value == "None" is just weird, better with empty string
-            meeting.set_official_schedule(None)
-        else:
-            schedule = get_schedule(meeting, value)
-            if not schedule.public:
-                return HttpResponse(status = 406)
-            #debug.log("3 meeting.schedule: %s" % (schedule))
-            meeting.set_official_schedule(schedule)
-
-    #debug.log("2 meeting.schedule: %s" % (meeting.schedule))
-    meeting.save()
-    return meeting_get(request, meeting)
-
-def meeting_json(request, num):
-    meeting = get_meeting(num)
-
-    if request.method == 'GET':
-        return meeting_get(request, meeting)
-    elif request.method == 'POST':
-        return meeting_update(request, meeting)
-    else:
-        return HttpResponse(status=406)
-
-
-#############################################################################
-## Session details API functions
-#############################################################################
-
-def session_json(request, num, sessionid):
-    meeting = get_meeting(num)
-
-    try:
-        session = meeting.session_set.get(pk=int(sessionid))
-    except Session.DoesNotExist:
-#        return json.dumps({'error':"no such session %s" % sessionid})
-        return HttpResponse(json.dumps({'error':"no such session %s" % sessionid}),
-                            status = 404,
-                            content_type="application/json")
-
-    sess1 = session.json_dict(request.build_absolute_uri('/'))
-    return HttpResponse(json.dumps(sess1, sort_keys=True, indent=2),
-                        content_type="application/json")
-
-# get group of all sessions.
-def sessions_json(request, num):
-    meeting = get_meeting(num)
-
-    sessions = meeting.session_set.that_can_meet().with_requested_time().with_requested_by()
-
-    sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in sessions ]
-    return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2),
-                        content_type="application/json")
-
-#############################################################################
-## Scheduledsesion
-#############################################################################
-
-# this creates an entirely *NEW* schedtimesessassignment
-def assignments_post(request, meeting, schedule):
-    cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user)
-    if not canedit:
-        return HttpResponse(json.dumps({'error':'no permission to modify this schedule'}),
-                            status = 403,
-                            content_type="application/json")
-
-    # get JSON out of raw body. XXX should check Content-Type!
-    newvalues = json.loads(request.body)
-    if not ("session_id" in newvalues) or not ("timeslot_id" in newvalues):
-        return HttpResponse(json.dumps({'error':'missing values, timeslot_id and session_id required'}),
-                            status = 406,
-                            content_type="application/json")
-
-    try:
-        Session.objects.get(pk=newvalues["session_id"])
-    except Session.DoesNotExist:
-        return HttpResponse(json.dumps({'error':'session has been deleted'}),
-                            status = 406,
-                            content_type="application/json")
-
-    ss1 = SchedTimeSessAssignment(schedule = schedule,
-                           session_id  = newvalues["session_id"],
-                           timeslot_id = newvalues["timeslot_id"])
-    if("extendedfrom_id" in newvalues):
-        val = int(newvalues["extendedfrom_id"])
-        try:
-            ss2 = schedule.assignments.get(pk = val)
-            ss1.extendedfrom = ss2
-        except SchedTimeSessAssignment.DoesNotExist:
-            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),
-                        status = 201,
-                        content_type="application/json")
-    # 201 code needs a Location: header.
-    response['Location'] = ss1_dict["href"],
-    return response
-
-def assignments_get(request, num, schedule):
-    assignments = schedule.assignments.all()
-
-    absolute_url = request.build_absolute_uri('/')
-    sess1_dict = [ x.json_dict(absolute_url) for x in assignments ]
-    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 schedule
-def assignments_json(request, num, owner, name):
-    info = get_meeting_schedule(num, owner, name)
-    # The return values from get_meeting_schedule() are silly, in that it
-    # is a tuple for non-error return, but a HTTPResponse when error, but
-    # work around that for the moment
-    if isinstance(info, HttpResponse):
-        return info
-    meeting, person, schedule = info
-
-    if request.method == 'GET':
-        return assignments_get(request, meeting, schedule)
-    elif request.method == 'POST':
-        return assignments_post(request, meeting, schedule)
-    else:
-        return HttpResponse(json.dumps({'error':'inappropriate action: %s' % (request.method)}),
-                            status = 406,
-                            content_type="application/json")
-
-# accepts both POST and PUT in order to implement Postel Doctrine.
-def assignment_update(request, meeting, schedule, ss):
-    cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user)
-    if not canedit:
-        return HttpResponse(json.dumps({'error':'no permission to update this schedule'}),
-                            status = 403,
-                            content_type="application/json")
-
-    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 assignment_delete(request, meeting, schedule, ss):
-    cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user)
-    if not canedit:
-        return HttpResponse(json.dumps({'error':'no permission to update this schedule'}),
-                            status = 403,
-                            content_type="application/json")
-
-    # in case there is, somehow, more than one item with the same pk.. XXX
-    assignments = schedule.assignments.filter(pk = ss.pk)
-    if len(assignments) == 0:
-        return HttpResponse(json.dumps({'error':'no such object'}),
-                            status = 404,
-                            content_type="application/json")
-    count=0
-    for ss in assignments:
-        ss.delete()
-        count += 1
-
-    return HttpResponse(json.dumps({'result':"%u objects deleted"%(count)}),
-                        status = 200,
-                        content_type="application/json")
-
-def assignment_get(request, meeting, schedule, ss):
-    cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user)
-
-    if not cansee:
-        return HttpResponse(json.dumps({'error':'no permission to see this schedule'}),
-                            status = 403,
-                            content_type="application/json")
-
-    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 return a specific session, updates a session or deletes a SPECIFIC scheduled session
-def assignment_json(request, num, owner, name, assignment_id):
-    meeting, person, schedule = get_meeting_schedule(num, owner, name)
-
-    assignments = schedule.assignments.filter(pk = assignment_id)
-    if len(assignments) == 0:
-        return HttpResponse(json.dumps({'error' : 'invalid assignment'}),
-                            content_type="application/json",
-                            status=404);
-    ss = assignments[0]
-
-    if request.method == 'GET':
-        return assignment_get(request, meeting, schedule, ss)
-    elif request.method == 'PUT' or request.method=='POST':
-        return assignment_update(request, meeting, schedule, ss)
-    elif request.method == 'DELETE':
-        return assignment_delete(request, meeting, schedule, ss)
-
-#############################################################################
-## Constraints API
-#############################################################################
-
-
-# Would like to cache for 1 day, but there are invalidation issues.
-#@cache_page(86400)
-def constraint_json(request, num, constraintid):
-    meeting = get_meeting(num)
-
-    try:
-        constraint = meeting.constraint_set.get(pk=int(constraintid))
-    except Constraint.DoesNotExist:
-        return HttpResponse(json.dumps({'error':"no such constraint %s" % constraintid}),
-                            status = 404,
-                            content_type="application/json")
-
-    json1 = constraint.json_dict(request.build_absolute_uri('/'))
-    return HttpResponse(json.dumps(json1, sort_keys=True, indent=2),
-                        content_type="application/json")
-
-
-# Cache for 2 hour2
-#@cache_page(7200)
-# caching is a problem if there Host: header changes.
-#
-def session_constraints(request, num, sessionid):
-    meeting = get_meeting(num)
-
-    #print "Getting meeting=%s session contraints for %s" % (num, sessionid)
-    try:
-        session = meeting.session_set.get(pk=int(sessionid))
-    except Session.DoesNotExist:
-        return json.dumps({"error":"no such session"})
-
-    constraint_list = session.constraints_dict(request.build_absolute_uri('/'))
-
-    json_str = json.dumps(constraint_list,
-                          sort_keys=True, indent=2),
-    #print "  gives: %s" % (json_str)
-
-    return HttpResponse(json_str, content_type="application/json")
-
-
-
diff --git a/ietf/meeting/helpers.py b/ietf/meeting/helpers.py
index 0ca6e0d12..1a7a3d5c2 100644
--- a/ietf/meeting/helpers.py
+++ b/ietf/meeting/helpers.py
@@ -9,13 +9,11 @@ import os
 import re
 from tempfile import mkstemp
 
-from django.http import HttpRequest, Http404
-from django.db.models import F, Max, Q, Prefetch
+from django.http import Http404
+from django.db.models import F, Prefetch
 from django.conf import settings
 from django.contrib.auth.models import AnonymousUser
-from django.core.cache import cache
 from django.urls import reverse
-from django.utils.cache import get_cache_key
 from django.shortcuts import get_object_or_404
 from django.template.loader import render_to_string
 
@@ -32,95 +30,12 @@ from ietf.meeting.models import Meeting, Schedule, TimeSlot, SchedTimeSessAssign
 from ietf.meeting.utils import session_requested_by, add_event_info_to_session_qs
 from ietf.name.models import ImportantDateName, SessionPurposeName
 from ietf.utils import log
-from ietf.utils.history import find_history_active_at, find_history_replacements_active_at
+from ietf.utils.history import find_history_replacements_active_at
 from ietf.utils.mail import send_mail
 from ietf.utils.pipe import pipe
 from ietf.utils.text import xslugify
 
 
-def find_ads_for_meeting(meeting):
-    ads = []
-    meeting_time = datetime.datetime.combine(meeting.date, datetime.time(0, 0, 0))
-
-    num = 0
-    # get list of ADs which are/were active at the time of the meeting.
-    #  (previous [x for x in y] syntax changed to aid debugging)
-    for g in Group.objects.filter(type="area").order_by("acronym"):
-        history = find_history_active_at(g, meeting_time)
-        num = num +1
-        if history and history != g:
-            #print " history[%u]: %s" % (num, history)
-            if history.state_id == "active":
-                for x in history.rolehistory_set.filter(name="ad",group__type='area').select_related('group', 'person', 'email'):
-                    #print "xh[%u]: %s" % (num, x)
-                    ads.append(x)
-        else:
-            #print " group[%u]: %s" % (num, g)
-            if g.state_id == "active":
-                for x in g.role_set.filter(name="ad",group__type='area').select_related('group', 'person', 'email'):
-                    #print "xg[%u]: %s (#%u)" % (num, x, x.pk)
-                    ads.append(x)
-    return ads
-
-
-# get list of all areas, + IRTF + IETF (plenaries).
-def get_pseudo_areas():
-    return Group.objects.filter(Q(state="active", name="IRTF")|
-                                Q(state="active", name="IETF")|
-                                Q(state="active", type="area")).order_by('acronym')
-
-# get list of all areas, + IRTF.
-def get_areas():
-    return Group.objects.filter(Q(state="active",
-                                  name="IRTF")|
-                                Q(state="active", type="area")).order_by('acronym')
-
-# get list of areas that are referenced.
-def get_area_list_from_sessions(assignments, num):
-    return assignments.filter(timeslot__type = 'regular',
-                                    session__group__parent__isnull = False).order_by(
-        'session__group__parent__acronym').distinct().values_list(
-        'session__group__parent__acronym',flat=True)
-
-def build_all_agenda_slices(meeting):
-    time_slices = []
-    date_slices = {}
-
-    for ts in meeting.timeslot_set.filter(type__in=['regular',]).order_by('time','name'):
-            ymd = ts.time.date()
-
-            if ymd not in date_slices and ts.location != None:
-                date_slices[ymd] = []
-                time_slices.append(ymd)
-
-            if ymd in date_slices:
-                if [ts.time, ts.time+ts.duration] not in date_slices[ymd]:   # only keep unique entries
-                    date_slices[ymd].append([ts.time, ts.time+ts.duration])
-
-    time_slices.sort()
-    return time_slices,date_slices
-
-def get_all_assignments_from_schedule(schedule):
-   ss = schedule.assignments.filter(timeslot__location__isnull = False)
-   ss = ss.filter(session__type__slug='regular')
-   ss = ss.order_by('timeslot__time','timeslot__name')
-
-   return ss
-
-def get_modified_from_assignments(assignments):
-    return assignments.aggregate(Max('timeslot__modified'))['timeslot__modified__max']
-
-def get_wg_name_list(assignments):
-    return assignments.filter(timeslot__type = 'regular',
-                                    session__group__isnull = False,
-                                    session__group__parent__isnull = False).order_by(
-        'session__group__acronym').distinct().values_list(
-        'session__group__acronym',flat=True)
-
-def get_wg_list(assignments):
-    wg_name_list = get_wg_name_list(assignments)
-    return Group.objects.filter(acronym__in = set(wg_name_list)).order_by('parent__acronym','acronym')
-
 def get_meeting(num=None,type_in=['ietf',],days=28):
     meetings = Meeting.objects
     if type_in:
@@ -799,15 +714,6 @@ def schedule_permissions(meeting, schedule, user):
 
     return cansee, canedit, secretariat
 
-def session_constraint_expire(request,session):
-    from .ajax import session_constraints
-    path = reverse(session_constraints, args=[session.meeting.number, session.pk])
-    temp_request = HttpRequest()
-    temp_request.path = path
-    temp_request.META['HTTP_HOST'] = request.META['HTTP_HOST']
-    key = get_cache_key(temp_request)
-    if key is not None and key in cache:
-        cache.delete(key)
 
 # -------------------------------------------------
 # Interim Meeting Helpers
diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py
index ea394569e..7abe1b1c6 100644
--- a/ietf/meeting/models.py
+++ b/ietf/meeting/models.py
@@ -14,7 +14,6 @@ import string
 
 from collections import namedtuple
 from pathlib import Path
-from urllib.parse import urljoin
 
 import debug                            # pyflakes:ignore
 
@@ -23,9 +22,6 @@ from django.db import models
 from django.db.models import Max, Subquery, OuterRef, TextField, Value, Q
 from django.db.models.functions import Coalesce
 from django.conf import settings
-# mostly used by json_dict()
-#from django.template.defaultfilters import slugify, date as date_format, time as time_format
-from django.template.defaultfilters import date as date_format
 from django.urls import reverse as urlreverse
 from django.utils.text import slugify
 from django.utils.safestring import mark_safe
@@ -310,35 +306,9 @@ class Meeting(models.Model):
             slugs = ('conflict', 'conflic2', 'conflic3')
         return ConstraintName.objects.filter(slug__in=slugs)
 
-    def json_url(self):
-        return "/meeting/%s/json" % (self.number, )
-
     def base_url(self):
         return "/meeting/%s" % (self.number, )
 
-    def json_dict(self, host_scheme):
-        # unfortunately, using the datetime aware json encoder seems impossible,
-        # so the dates are formatted as strings here.
-        agenda_url = ""
-        if self.schedule:
-            agenda_url = urljoin(host_scheme, self.schedule.base_url())
-        return {
-            'href':                 urljoin(host_scheme, self.json_url()),
-            'name':                 self.number,
-            'submission_start_date':   fmt_date(self.get_submission_start_date()),
-            'submission_cut_off_date': fmt_date(self.get_submission_cut_off_date()),
-            'submission_correction_date': fmt_date(self.get_submission_correction_date()),
-            'date':                    fmt_date(self.date),
-            'agenda_href':             agenda_url,
-            'city':                    self.city,
-            'country':                 self.country,
-            'time_zone':               self.time_zone,
-            'venue_name':              self.venue_name,
-            'venue_addr':              self.venue_addr,
-            'break_area':              self.break_area,
-            'reg_area':                self.reg_area
-            }
-
     def build_timeslices(self):
         """Get unique day/time/timeslot data for meeting
         
@@ -438,13 +408,6 @@ class ResourceAssociation(models.Model):
     def __str__(self):
         return self.desc
 
-    def json_dict(self, host_scheme):
-        res1 = dict()
-        res1['name'] = self.name.slug
-        res1['icon'] = "/images/%s" % (self.icon)
-        res1['desc'] = self.desc
-        res1['resource_id'] = self.pk
-        return res1
 
 class Room(models.Model):
     meeting = ForeignKey(Meeting)
@@ -487,15 +450,6 @@ class Room(models.Model):
     def dom_id(self):
         return "room%u" % (self.pk)
 
-    def json_url(self):
-        return "/meeting/%s/room/%s.json" % (self.meeting.number, self.id)
-
-    def json_dict(self, host_scheme):
-        return {
-            'href':                 urljoin(host_scheme, self.json_url()),
-            'name':                 self.name,
-            'capacity':             self.capacity,
-            }
     # floorplan support
     def floorplan_url(self):
         mtg_num = self.meeting.get_number()
@@ -700,29 +654,8 @@ class TimeSlot(models.Model):
             dom_id = self.location.dom_id()
         return "%s_%s_%s" % (dom_id, self.time.strftime('%Y-%m-%d'), self.time.strftime('%H%M'))
 
-    def json_dict(self, host_scheme):
-        ts = dict()
-        ts['timeslot_id'] = self.id
-        ts['href']        = urljoin(host_scheme, self.json_url())
-        ts['room']        = self.get_location()
-        ts['roomtype'] = self.type.slug
-        if self.location is not None:
-            ts['capacity'] = self.location.capacity
-        ts["time"]     = date_format(self.time, 'Hi')
-        ts["date"]     = fmt_date(self.time)
-        ts["domid"]    = self.js_identifier
-        following = self.slot_to_the_right
-        if following is not None:
-            ts["following_timeslot_id"] = following.id
-        return ts
-
-    def json_url(self):
-        return "/meeting/%s/timeslot/%s.json" % (self.meeting.number, self.id)
-
-    """
-    This routine deletes all timeslots which are in the same time as this slot.
-    """
     def delete_concurrent_timeslots(self):
+        """Delete all timeslots which are in the same time as this slot"""
         # can not include duration in filter, because there is no support
         # for having it a WHERE clause.
         # below will delete self as well.
@@ -826,25 +759,6 @@ class Schedule(models.Model):
     def delete_assignments(self):
         self.assignments.all().delete()
 
-    def json_url(self):
-        return "%s.json" % self.base_url()
-
-    def json_dict(self, host_scheme):
-        sch = dict()
-        sch['schedule_id'] = self.id
-        sch['href']        = urljoin(host_scheme, self.json_url())
-        if self.visible:
-            sch['visible']  = "visible"
-        else:
-            sch['visible']  = "hidden"
-        if self.public:
-            sch['public']   = "public"
-        else:
-            sch['public']   = "private"
-        sch['owner']       = urljoin(host_scheme, self.owner.json_url())
-        # should include href to list of assignments, but they have no direct API yet.
-        return sch
-
     @property
     def qs_assignments_with_sessions(self):
         return self.assignments.filter(session__isnull=False)
@@ -908,40 +822,6 @@ class SchedTimeSessAssignment(models.Model):
         """Get the TimeSlotTypeName that applies to this assignment"""
         return self.timeslot.type
 
-    def json_url(self):
-        if not hasattr(self, '_cached_json_url'):
-            self._cached_json_url =  "/meeting/%s/agenda/%s/%s/session/%u.json" % (
-                                        self.schedule.meeting.number,
-                                        self.schedule.owner_email(),
-                                        self.schedule.name, self.id )
-        return self._cached_json_url
-
-    def json_dict(self, host_scheme):
-        if not hasattr(self, '_cached_json_dict'):
-            ss = dict()
-            ss['assignment_id'] = self.id
-            ss['href']          = urljoin(host_scheme, self.json_url())
-            ss['timeslot_id'] = self.timeslot.id
-
-            efset = self.session.timeslotassignments.filter(schedule=self.schedule).order_by("timeslot__time")
-            if efset.count() > 1:
-                # now we know that there is some work to do finding the extendedfrom_id.
-                # loop through the list of items
-                previous = None
-                for efss in efset:
-                    if efss.pk == self.pk:
-                        extendedfrom = previous
-                        break
-                    previous = efss
-                if extendedfrom is not None:
-                    ss['extendedfrom_id']  = extendedfrom.id
-
-            if self.session:
-                ss['session_id']  = self.session.id
-            ss["pinned"]   = self.pinned
-            self._cached_json_dict = ss
-        return self._cached_json_dict
-
     def slug(self):
         """Return sensible id string for session, e.g. suitable for use as HTML anchor."""
         components = []
@@ -1033,30 +913,6 @@ class Constraint(models.Model):
         elif not self.target and self.person:
             return "%s " % (self.person)
 
-    def json_url(self):
-        return "/meeting/%s/constraint/%s.json" % (self.meeting.number, self.id)
-
-    def json_dict(self, host_scheme):
-        ct1 = dict()
-        ct1['constraint_id'] = self.id
-        ct1['href']          = urljoin(host_scheme, self.json_url())
-        ct1['name']          = self.name.slug
-        if self.person is not None:
-            ct1['person_href'] = urljoin(host_scheme, self.person.json_url())
-        if self.source is not None:
-            ct1['source_href'] = urljoin(host_scheme, self.source.json_url())
-        if self.target is not None:
-            ct1['target_href'] = urljoin(host_scheme, self.target.json_url())
-        ct1['meeting_href'] = urljoin(host_scheme, self.meeting.json_url())
-        if self.time_relation:
-            ct1['time_relation'] = self.time_relation
-            ct1['time_relation_display'] = self.get_time_relation_display()
-        if self.timeranges.count():
-            ct1['timeranges_cant_meet'] = [t.slug for t in self.timeranges.all()]
-            timeranges_str = ", ".join([t.desc for t in self.timeranges.all()])
-            ct1['timeranges_display'] = "Can't meet %s" % timeranges_str
-        return ct1
-
 
 class SessionPresentation(models.Model):
     session = ForeignKey('Session')
@@ -1367,92 +1223,10 @@ class Session(models.Model):
     def official_timeslotassignment(self):
         return self.timeslotassignments.filter(schedule__in=[self.meeting.schedule, self.meeting.schedule.base if self.meeting.schedule else None]).first()
 
-    def constraints_dict(self, host_scheme):
-        constraint_list = []
-        for constraint in self.constraints():
-            ct1 = constraint.json_dict(host_scheme)
-            constraint_list.append(ct1)
-
-        for constraint in self.reverse_constraints():
-            ct1 = constraint.json_dict(host_scheme)
-            constraint_list.append(ct1)
-        return constraint_list
-
     @property
     def people_constraints(self):
         return self.group.constraint_source_set.filter(meeting=self.meeting, name='bethere')
 
-    def json_url(self):
-        return "/meeting/%s/session/%s.json" % (self.meeting.number, self.id)
-
-    def json_dict(self, host_scheme):
-        sess1 = dict()
-        sess1['href']           = urljoin(host_scheme, self.json_url())
-        if self.group is not None:
-            sess1['group']          = self.group.json_dict(host_scheme)
-            sess1['group_href']     = urljoin(host_scheme, self.group.json_url())
-            if self.group.parent is not None:
-                sess1['area']           = self.group.parent.acronym.upper()
-            sess1['description']    = self.group.name
-            sess1['group_id']       = str(self.group.pk)
-        reslist = []
-        for r in self.resources.all():
-            reslist.append(r.json_dict(host_scheme))
-        sess1['resources']      = reslist
-        sess1['session_id']     = str(self.pk)
-        sess1['name']           = self.name
-        sess1['title']          = self.short_name
-        sess1['short_name']     = self.short_name
-        sess1['bof']            = str(self.group.is_bof())
-        sess1['agenda_note']    = self.agenda_note
-        sess1['attendees']      = str(self.attendees)
-        sess1['joint_with_groups'] = self.joint_with_groups_acronyms()
-
-        # fish out scheduling information - eventually, we should pick
-        # this out in the caller instead
-        latest_event = None
-        first_event = None
-
-        if self.pk is not None:
-            if not hasattr(self, 'current_status') or not hasattr(self, 'requested_time'):
-                events = list(SchedulingEvent.objects.filter(session=self.pk).order_by('time', 'id'))
-                if events:
-                    first_event = events[0]
-                    latest_event = events[-1]
-
-        status_id = None
-        if hasattr(self, 'current_status'):
-            status_id = self.current_status
-        elif latest_event:
-            status_id = latest_event.status_id
-
-        sess1['status']         = SessionStatusName.objects.get(slug=status_id).name if status_id else None
-        if self.comments is not None:
-            sess1['comments']       = self.comments
-
-        requested_time = None
-        if hasattr(self, 'requested_time'):
-            requested_time = self.requested_time
-        elif first_event:
-            requested_time = first_event.time
-        sess1['requested_time'] = requested_time.strftime("%Y-%m-%d") if requested_time else None
-
-
-        requested_by = None
-        if hasattr(self, 'requested_by'):
-            requested_by = self.requested_by
-        elif first_event:
-            requested_by = first_event.by_id
-
-        if requested_by is not None:
-            requested_by_person = Person.objects.filter(pk=requested_by).first()
-            if requested_by_person:
-                sess1['requested_by']   = str(requested_by_person)
-
-        sess1['requested_duration']= "%.1f" % (float(self.requested_duration.seconds) / 3600)
-        sess1['special_request'] = str(self.special_request_token)
-        return sess1
-
     def agenda_text(self):
         doc = self.agenda()
         if doc:
diff --git a/ietf/meeting/tests_api.py b/ietf/meeting/tests_api.py
deleted file mode 100644
index c7d010dc5..000000000
--- a/ietf/meeting/tests_api.py
+++ /dev/null
@@ -1,501 +0,0 @@
-# Copyright The IETF Trust 2013-2020, All Rights Reserved
-# -*- coding: utf-8 -*-
-
-
-import datetime
-
-from urllib.parse import urlsplit
-
-from django.urls import reverse as urlreverse
-
-import debug                            # pyflakes:ignore
-
-from ietf.name.models import TimerangeName
-from ietf.group.models import Group
-from ietf.meeting.models import Schedule, TimeSlot, Session, SchedTimeSessAssignment, Meeting, Constraint
-from ietf.meeting.test_data import make_meeting_test_data
-from ietf.person.models import Person
-from ietf.utils.test_utils import TestCase
-from ietf.utils.mail import outbox
-
-
-class ApiTests(TestCase):
-    def test_update_schedule(self):
-        meeting = make_meeting_test_data()
-        schedule = Schedule.objects.get(meeting__number=72,name="test-schedule")
-        mars_session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
-        ames_session = Session.objects.filter(meeting=meeting, group__acronym="ames").first()
-    
-        mars_scheduled = SchedTimeSessAssignment.objects.get(session=mars_session,schedule__name='test-schedule')
-        mars_slot = mars_scheduled.timeslot 
-
-        ames_scheduled = SchedTimeSessAssignment.objects.get(session=ames_session,schedule__name='test-schedule')
-        ames_slot = ames_scheduled.timeslot 
-
-        def do_unschedule(assignment):
-            url = urlreverse("ietf.meeting.ajax.assignment_json", 
-                             kwargs=dict(num=assignment.session.meeting.number, 
-                                         owner=assignment.schedule.owner_email(),
-                                         name=assignment.schedule.name,
-                                         assignment_id=assignment.pk,))
-            return self.client.delete(url)
-
-        def do_schedule(schedule,session,timeslot):
-            url = urlreverse("ietf.meeting.ajax.assignments_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')
-
-        def do_extend(schedule, assignment):
-            session = assignment.session
-            url = urlreverse("ietf.meeting.ajax.assignments_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,assignment.timeslot.slot_to_the_right.pk,assignment.pk)
-
-
-            return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded')
-
-        # not logged in
-        # faulty delete 
-        r = do_unschedule(mars_scheduled)
-        self.assertEqual(r.status_code, 403)
-        self.assertEqual(SchedTimeSessAssignment.objects.get(pk=mars_scheduled.pk).session, mars_session)
-        # faulty post
-        r = do_schedule(schedule,ames_session,mars_slot)
-        self.assertEqual(r.status_code, 403)
-
-        # logged in as non-owner
-        # faulty delete
-        self.client.login(username="ad", password="ad+password")
-        r = do_unschedule(mars_scheduled)
-        self.assertEqual(r.status_code, 403)
-        self.assertTrue("error" in r.json())
-        # faulty post
-        r = do_schedule(schedule,ames_session,mars_slot)
-        self.assertEqual(r.status_code, 403)
-
-        # Put ames in the same timeslot as mars
-        self.client.login(username="plain", password='plain+password')
-        r = do_unschedule(ames_scheduled)
-        self.assertEqual(r.status_code, 200)
-        self.assertNotIn("error", r.json())
-
-        r = do_schedule(schedule,ames_session,mars_slot)
-        self.assertEqual(r.status_code, 201)
-
-        # Move the two timeslots close enough together for extension to work
-        ames_slot_qs=TimeSlot.objects.filter(id=ames_slot.id)
-        ames_slot_qs.update(time=mars_slot.time+mars_slot.duration+datetime.timedelta(minutes=10))
-        
-        # Extend the mars session
-        r = do_extend(schedule,mars_scheduled)
-        self.assertEqual(r.status_code, 201)
-        self.assertTrue("error" not in r.json())
-        self.assertEqual(mars_session.timeslotassignments.filter(schedule__name='test-schedule').count(),2)
-
-        # Unschedule mars 
-        r = do_unschedule(mars_scheduled)
-        self.assertEqual(r.status_code, 200)
-        self.assertNotIn("error", r.json())
-        # Make sure it got both the original and extended session
-        self.assertEqual(mars_session.timeslotassignments.filter(schedule__name='test-schedule').count(),0)
-
-        self.assertEqual(SchedTimeSessAssignment.objects.get(session=ames_session,schedule__name='test-schedule').timeslot, mars_slot)
-
-
-    def test_constraints_json(self):
-        meeting = make_meeting_test_data()
-        session = Session.objects.filter(meeting=meeting, group__acronym="mars").select_related("group").first()
-        c_ames = Constraint.objects.create(meeting=meeting, source=session.group,
-                                           target=Group.objects.get(acronym="ames"),
-                                           name_id="conflict")
-
-        c_person = Constraint.objects.create(meeting=meeting, source=session.group,
-                                             person=Person.objects.get(user__username="ad"),
-                                             name_id="bethere")
-
-        c_adjacent = Constraint.objects.create(meeting=meeting, source=session.group,
-                                              target=Group.objects.get(acronym="irg"),
-                                              name_id="wg_adjacent")
-
-        c_time_relation = Constraint.objects.create(meeting=meeting, source=session.group,
-                                                    time_relation='subsequent-days',
-                                                    name_id="time_relation")
-
-        c_timerange = Constraint.objects.create(meeting=meeting, source=session.group,
-                                                name_id="timerange")
-        c_timerange.timeranges.set(TimerangeName.objects.filter(slug__startswith='monday'))
-
-        r = self.client.get(urlreverse("ietf.meeting.ajax.session_constraints", kwargs=dict(num=meeting.number, sessionid=session.pk)))
-        self.assertEqual(r.status_code, 200)
-        constraints = r.json()
-        expected_keys = set([c_ames.pk, c_person.pk, c_adjacent.pk, c_time_relation.pk, c_timerange.pk])
-        self.assertEqual(expected_keys, set(c["constraint_id"] for c in constraints))
-
-    def test_meeting_json(self):
-        meeting = make_meeting_test_data()
-
-        r = self.client.get(urlreverse("ietf.meeting.ajax.meeting_json", kwargs=dict(num=meeting.number)))
-        self.assertEqual(r.status_code, 200)
-        info = r.json()
-        self.assertEqual(info["name"], meeting.number)
-
-    def test_get_room_json(self):
-        meeting = make_meeting_test_data()
-        room = meeting.room_set.first()
-
-        r = self.client.get(urlreverse("ietf.meeting.ajax.timeslot_roomurl", kwargs=dict(num=meeting.number, roomid=room.pk)))
-        self.assertEqual(r.status_code, 200)
-        info = r.json()
-        self.assertEqual(info["name"], room.name)
-
-    def test_create_new_room(self):
-        meeting = make_meeting_test_data()
-        timeslots_before = meeting.timeslot_set.filter(type='regular').count()
-        url = urlreverse("ietf.meeting.ajax.timeslot_roomsurl", kwargs=dict(num=meeting.number))
-
-        post_data = { "name": "new room", "capacity": "50" , "resources": [], "session_types":['regular']}
-
-        # unauthorized post
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 302)
-        self.assertTrue(not meeting.room_set.filter(name="new room"))
-
-        # create room
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 302)
-        self.assertTrue(meeting.room_set.filter(name="new room"))
-
-        timeslots_after = meeting.timeslot_set.filter(type='regular').count()
-        # It's not clear that what that ajax function is doing is the right thing to do,
-        # but it currently makes a new timeslot for any existing timeslot.
-        # The condition tested below relies on the timeslots before this test all having different start and end times
-        self.assertEqual( timeslots_after, 2 * timeslots_before)
-
-    def test_delete_room(self):
-        meeting = make_meeting_test_data()
-        room = meeting.room_set.first()
-        timeslots_before = list(room.timeslot_set.values_list("pk", flat=True))
-
-        url = urlreverse("ietf.meeting.ajax.timeslot_roomurl", kwargs=dict(num=meeting.number, roomid=room.pk))
-
-        # unauthorized delete
-        r = self.client.delete(url)
-        self.assertEqual(r.status_code, 302)
-        self.assertTrue(meeting.room_set.filter(pk=room.pk))
-
-        # delete
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.delete(url)
-        self.assertTrue(not meeting.room_set.filter(pk=room.pk))
-        self.assertTrue(not TimeSlot.objects.filter(pk__in=timeslots_before))
-
-    # This really belongs in group tests
-    def test_group_json(self):
-        make_meeting_test_data()
-        group = Group.objects.get(acronym="mars")
-
-        url = urlreverse("ietf.group.views.group_json", kwargs=dict(acronym=group.acronym))
-        r = self.client.get(url)
-        self.assertEqual(r.status_code, 200)
-        info = r.json()
-        self.assertEqual(info["name"], group.name)
-
-    # This really belongs in person tests
-    def test_person_json(self):
-        make_meeting_test_data()
-        person = Person.objects.get(user__username="ad")
-
-        url = urlreverse("ietf.person.ajax.person_json", kwargs=dict(personid=person.pk))
-        r = self.client.get(url)
-        self.assertEqual(r.status_code, 200)
-        info = r.json()
-        self.assertEqual(info["name"], person.name)
-
-    def test_sessions_json(self):
-        meeting = make_meeting_test_data()
- 
-        url = urlreverse("ietf.meeting.ajax.sessions_json",kwargs=dict(num=meeting.number))
-        r = self.client.get(url)
-        self.assertEqual(r.status_code, 200)
-        info = r.json()
-        self.assertEqual(set([x['short_name'] for x in info]),set([s.session.short_name for s in meeting.schedule.assignments.filter(session__type_id='regular')]))
-
-        schedule = meeting.schedule
-        url = urlreverse("ietf.meeting.ajax.assignments_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 = r.json()
-        self.assertEqual(len(info),schedule.assignments.count())
-
-
-    def test_slot_json(self):
-        meeting = make_meeting_test_data()
-        slot = meeting.timeslot_set.all()[0]
-
-        url = urlreverse("ietf.meeting.ajax.timeslot_sloturl",
-                         kwargs=dict(num=meeting.number, slotid=slot.pk))
-        r = self.client.get(url)
-        self.assertEqual(r.status_code, 200)
-        info = r.json()
-        self.assertEqual(info["timeslot_id"], slot.pk)
-
-    def test_create_new_slot(self):
-        meeting = make_meeting_test_data()
-
-        slot_time = datetime.date.today()
-
-        url = urlreverse("ietf.meeting.ajax.timeslot_slotsurl",
-                         kwargs=dict(num=meeting.number))
-        post_data = {
-            'type' : 'plenary',
-            'time' : slot_time.strftime("%Y-%m-%d"),
-            'duration': '08:00:00',
-        }
-
-        # unauthorized post
-        prior_slotcount = meeting.timeslot_set.count()
-        self.client.login(username="ad", password="ad+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 403)
-        self.assertEqual(meeting.timeslot_set.count(),prior_slotcount)
-
-        # create slot
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 201)
-        self.assertTrue(meeting.timeslot_set.filter(time=slot_time))
-        self.assertEqual(meeting.timeslot_set.count(),prior_slotcount+1)
-
-    def test_delete_slot(self):
-        meeting = make_meeting_test_data()
-        slot = meeting.timeslot_set.all()[0]
-
-        url = urlreverse("ietf.meeting.ajax.timeslot_sloturl",
-                         kwargs=dict(num=meeting.number, slotid=slot.pk))
-
-        # unauthorized delete
-        self.client.login(username="ad", password="ad+password")
-        r = self.client.delete(url)
-        self.assertEqual(r.status_code, 403)
-
-        # delete
-        self.client.login(username="secretary", password="secretary+password")
-        self.client.delete(url)
-        self.assertTrue(not meeting.timeslot_set.filter(pk=slot.pk))
-
-    def test_schedule_json(self):
-        meeting = make_meeting_test_data()
-
-        url = urlreverse("ietf.meeting.ajax.schedule_infourl",
-                         kwargs=dict(num=meeting.number,
-                                     owner=meeting.schedule.owner_email(),
-                                     name=meeting.schedule.name))
-
-        r = self.client.get(url)
-        info = r.json()
-        self.assertEqual(info["schedule_id"], meeting.schedule.pk)
-
-    def test_create_new_schedule(self):
-        meeting = make_meeting_test_data()
-
-        url = urlreverse("ietf.meeting.ajax.schedule_infosurl",
-                         kwargs=dict(num=meeting.number))
-        post_data = {
-            'name': 'new-schedule',
-        }
-
-        # unauthorized post
-        self.client.login(username="plain", password="plain+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 403)
-        self.assertTrue(not meeting.schedule_set.filter(name='new-schedule'))
-
-        # create new schedule
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 302)
-        self.assertTrue(meeting.schedule_set.filter(name='new-schedule'))
-
-    def test_update_meeting_schedule(self):
-        meeting = make_meeting_test_data()
-
-        self.assertTrue(meeting.schedule.visible)
-
-        url = urlreverse("ietf.meeting.ajax.schedule_infourl",
-                         kwargs=dict(num=meeting.number,
-                                     owner=meeting.schedule.owner_email(),
-                                     name=meeting.schedule.name))
-
-        post_data = {
-            'visible': 'false',
-            'name': 'new-test-name',
-        }
-
-        # unauthorized posts
-        self.client.logout()
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 403)
-        self.client.login(username="ad", password="ad+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 403)
-
-        # change schedule
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 302)
-        changed_schedule = Schedule.objects.get(pk=meeting.schedule.pk)
-        self.assertTrue(not changed_schedule.visible)
-        self.assertEqual(changed_schedule.name, "new-test-name")
-
-    def test_delete_schedule(self):
-        meeting = make_meeting_test_data()
-
-        url = urlreverse("ietf.meeting.ajax.schedule_infourl",
-                         kwargs=dict(num=meeting.number,
-                                     owner=meeting.schedule.owner_email(),
-                                     name=meeting.schedule.name))
-        # unauthorized delete
-        self.client.login(username="plain", password="plain+password")
-        r = self.client.delete(url)
-        self.assertEqual(r.status_code, 403)
-
-        # delete
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.delete(url)
-        self.assertEqual(r.status_code, 200)
-        self.assertTrue(not Schedule.objects.filter(pk=meeting.schedule.pk))
-
-    def test_set_meeting_schedule(self):
-        meeting = make_meeting_test_data()
-        schedule = meeting.schedule
-
-        url = urlreverse("ietf.meeting.ajax.meeting_json",
-                         kwargs=dict(num=meeting.number))
-        post_data = {
-            "schedule": "",
-            }
-        # unauthorized post
-        self.client.login(username="ad", password="ad+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 403)
-
-        # clear
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 200)
-        self.assertTrue(not Meeting.objects.get(pk=meeting.pk).schedule)
-
-        # set schedule - first fail with non-public
-        post_data = {
-            "schedule": schedule.name,
-            }
-        schedule.public = False
-        schedule.save()
-
-        r = self.client.post(url, post_data)
-        self.assertTrue(r.status_code != 200)
-        self.assertTrue(not Meeting.objects.get(pk=meeting.pk).schedule)
-
-        # then go through with public
-        schedule.public = True
-        schedule.save()
-
-        # Setting a meeting as official no longer sends mail immediately
-        prior_length= len(outbox)
-        r = self.client.post(url, post_data)
-        self.assertEqual(r.status_code, 200)
-        self.assertEqual(Meeting.objects.get(pk=meeting.pk).schedule, schedule)
-        self.assertEqual(len(outbox),prior_length)
-
-    def test_read_only(self):
-        meeting = make_meeting_test_data()
-
-        # Secretariat
-        self.client.login(username="secretary", password="secretary+password")
-        url = '/meeting/%s/agenda/%s/%s/permissions' % (meeting.number, meeting.schedule.owner.email_address(), meeting.schedule.name);
-        r = self.client.get(url)
-        self.assertEqual(r.status_code, 200)
-
-        info = r.json()
-        self.assertEqual(info['secretariat'], True)
-        self.assertEqual(urlsplit(info['owner_href'])[2], "/person/%s.json" % meeting.schedule.owner_id)
-        self.assertEqual(info['read_only'], True)
-        self.assertEqual(info['save_perm'], True)
-
-        # owner
-        self.client.login(username=meeting.schedule.owner.user.username,
-                          password=meeting.schedule.owner.user.username+"+password")
-        url = '/meeting/%s/agenda/%s/%s/permissions' % (meeting.number, meeting.schedule.owner.email_address(), meeting.schedule.name);
-        r = self.client.get(url)
-        self.assertEqual(r.status_code, 200)
-
-        info = r.json()
-        self.assertEqual(info['secretariat'], False)
-        self.assertEqual(info['read_only'], False)
-        self.assertEqual(info['save_perm'], False)
-
-    def test_update_timeslot_pinned(self):
-        meeting = make_meeting_test_data()
-        scheduled = SchedTimeSessAssignment.objects.filter(
-            session__meeting=meeting, session__group__acronym="mars").first()
-
-        url = '/meeting/%s/agenda/%s/%s/session/%u.json' % (meeting.number, meeting.schedule.owner_email(), meeting.schedule.name, scheduled.pk)
-
-        post_data = {
-            "pinned": True
-            }
-
-        # 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 SchedTimeSessAssignment.objects.get(pk=scheduled.pk).pinned)
-
-        # set pinned
-        meeting.schedule.owner = Person.objects.get(user__username="secretary")
-        meeting.schedule.save()
-
-        # need to rebuild URL, since the schedule owner has changed.
-        url = '/meeting/%s/agenda/%s/%s/session/%u.json' % (meeting.number, meeting.schedule.owner_email(), meeting.schedule.name, scheduled.pk)
-
-        self.client.login(username="secretary", password="secretary+password")
-        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(SchedTimeSessAssignment.objects.get(pk=scheduled.pk).pinned)
-
-class TimeSlotEditingApiTests(TestCase):
-
-    def test_manipulate_timeslot(self):
-        meeting = make_meeting_test_data()
-        slot = meeting.timeslot_set.filter(type_id='regular')[0]
-
-        url = urlreverse("ietf.meeting.ajax.timeslot_sloturl",
-                         kwargs=dict(num=meeting.number, slotid=slot.pk))
-
-        modify_post_data = {
-            "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, 403)
-        slot.refresh_from_db()
-        self.assertEqual(slot.type_id, 'regular')
-
-        # 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)
-        slot.refresh_from_db()
-        self.assertEqual(slot.type_id, 'plenary')
diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py
index 14dfaf5f0..7d2cc2d0a 100644
--- a/ietf/meeting/tests_js.py
+++ b/ietf/meeting/tests_js.py
@@ -854,7 +854,7 @@ class ScheduleEditTests(IetfSeleniumTestCase):
         ss = list(SchedTimeSessAssignment.objects.filter(session__meeting__number=72,session__group__acronym='mars',schedule__name='test-schedule')) # pyflakes:ignore
 
         self.login()
-        url = self.absreverse('ietf.meeting.views.edit_schedule',kwargs=dict(num='72',name='test-schedule',owner='plain@example.com'))
+        url = self.absreverse('ietf.meeting.views.edit_meeting_schedule',kwargs=dict(num='72',name='test-schedule',owner='plain@example.com'))
         self.driver.get(url)
 
         # driver.get() will wait for scripts to finish, but not ajax
@@ -2770,5 +2770,5 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
 #        make_meeting_test_data()
 #
 #    def testOpenSchedule(self):
-#        url = urlreverse('ietf.meeting.views.edit_schedule', kwargs=dict(num='72',name='test-schedule'))
+#        url = urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs=dict(num='72',name='test-schedule'))
 #        r = self.client.get(url)
diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py
index cae8d2a39..7dc1010c5 100644
--- a/ietf/meeting/tests_views.py
+++ b/ietf/meeting/tests_views.py
@@ -3053,6 +3053,7 @@ class ReorderSlidesTests(TestCase):
 
 
 class EditTests(TestCase):
+    """Test schedule edit operations"""
     def setUp(self):
         # make sure we have the colors of the area
         from ietf.group.colors import fg_group_colors, bg_group_colors
@@ -3060,17 +3061,6 @@ class EditTests(TestCase):
         fg_group_colors[area_upper] = "#333"
         bg_group_colors[area_upper] = "#aaa"
 
-    def test_edit_schedule(self):
-        meeting = make_meeting_test_data()
-
-        self.client.login(username="secretary", password="secretary+password")
-        r = self.client.get(urlreverse("ietf.meeting.views.edit_schedule", kwargs={'num': meeting.number}))
-        self.assertRedirects(
-            r,
-            urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs={'num': meeting.number}),
-            status_code=301,
-        )
-
     def test_official_record_schedule_is_read_only(self):
         def _set_date_offset_and_retrieve_page(meeting, days_offset, client):
             meeting.date = datetime.date.today() + datetime.timedelta(days=days_offset)
@@ -3598,8 +3588,8 @@ class EditTests(TestCase):
         self.assertEqual(tostring(s2_constraints[1][0]), conf_label)  # [0][0] is the innermost <span>
 
     def test_new_meeting_schedule(self):
+        """Can create a meeting schedule from scratch"""
         meeting = make_meeting_test_data()
-
         self.client.login(username="secretary", password="secretary+password")
 
         # new from scratch
@@ -3623,7 +3613,11 @@ class EditTests(TestCase):
         self.assertEqual(new_schedule.origin, None)
         self.assertEqual(new_schedule.base_id, meeting.schedule.base_id)
 
-        # copy
+    def test_copy_meeting_schedule(self):
+        """Can create a copy of an existing meeting schedule"""
+        meeting = make_meeting_test_data()
+        self.client.login(username="secretary", password="secretary+password")
+
         url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number, owner=meeting.schedule.owner_email(), name=meeting.schedule.name))
         r = self.client.get(url)
         self.assertEqual(r.status_code, 200)
@@ -3647,34 +3641,21 @@ class EditTests(TestCase):
         for a in SchedTimeSessAssignment.objects.filter(schedule=new_schedule):
             self.assertIn((a.session_id, a.timeslot_id), old_assignments)
 
-    def test_save_agenda_as_and_read_permissions(self):
+    def test_schedule_read_permissions(self):
         meeting = make_meeting_test_data()
+        schedule = meeting.schedule
 
         # try to get non-existing agenda
-        url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number,
-                                                                       owner=meeting.schedule.owner_email(),
-                                                                       name="foo"))
+        url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number,
+                                                                                 owner=schedule.owner_email(),
+                                                                                 name="foo"))
         r = self.client.get(url)
         self.assertEqual(r.status_code, 404)
 
-        # save as new name (requires valid existing agenda)
-        url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number,
-                                                                       owner=meeting.schedule.owner_email(),
-                                                                       name=meeting.schedule.name))
-        self.client.login(username="ad", password="ad+password")
-        r = self.client.post(url, {
-            'savename': "foo",
-            'saveas': "saveas",
-            })
-        self.assertEqual(r.status_code, 302)
-        # Verify that we actually got redirected to a new place.
-        self.assertNotEqual(urlparse(r.url).path, url)
-
-        # get
-        schedule = meeting.get_schedule_by_name("foo")
-        url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number,
-                                                                       owner=schedule.owner_email(),
-                                                                       name="foo"))
+        url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number,
+                                                                                 owner=schedule.owner_email(),
+                                                                                 name=schedule.name))
+        self.client.login(username='ad', password='ad+password')
         r = self.client.get(url)
         self.assertEqual(r.status_code, 200)
 
@@ -3701,39 +3682,73 @@ class EditTests(TestCase):
         r = self.client.get(url)
         self.assertEqual(r.status_code, 200)
 
-    def test_save_agenda_broken_names(self):
+    def test_new_meeting_schedule_rejects_invalid_names(self):
         meeting = make_meeting_test_data()
 
-        # save as new name (requires valid existing agenda)
-        url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number,
-                                                                       owner=meeting.schedule.owner_email(),
-                                                                       name=meeting.schedule.name))
+        orig_schedule_count = meeting.schedule_set.count()
         self.client.login(username="ad", password="ad+password")
+        url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number))
         r = self.client.post(url, {
-            'savename': "/no/this/should/not/work/it/is/too/long",
-            'saveas': "saveas",
-            })
-        self.assertEqual(r.status_code, 302)
-        self.assertEqual(urlparse(r.url).path, url)
-        # TODO: Verify that an error message was in fact returned.
+            'name': "/no/this/should/not/work/it/is/too/long",
+            'public': "on",
+            'notes': "Name too long",
+            'base': meeting.schedule.base_id,
+        })
+        self.assertEqual(r.status_code, 200)
+        self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
+        self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
 
         r = self.client.post(url, {
-            'savename': "/invalid/chars/",
-            'saveas': "saveas",
-            })
-        # TODO: Verify that an error message was in fact returned.
-        self.assertEqual(r.status_code, 302)
-        self.assertEqual(urlparse(r.url).path, url)
+            'name': "/invalid/chars/",
+            'public': "on",
+            'notes': "Name too long",
+            'base': meeting.schedule.base_id,
+        })
+        self.assertEqual(r.status_code, 200)
+        self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
+        self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
 
         # Non-ASCII alphanumeric characters
         r = self.client.post(url, {
-            'savename': "f\u00E9ling",
-            'saveas': "saveas",
-            })
-        # TODO: Verify that an error message was in fact returned.
-        self.assertEqual(r.status_code, 302)
-        self.assertEqual(urlparse(r.url).path, url)
-        
+            'name': "f\u00E9ling",
+            'public': "on",
+            'notes': "Name too long",
+            'base': meeting.schedule.base_id,
+        })
+        self.assertEqual(r.status_code, 200)
+        self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
+        self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
+
+    def test_edit_session(self):
+        session = SessionFactory(group__type_id='team')  # type determines allowed session purposes
+        self.client.login(username='secretary', password='secretary+password')
+        url = urlreverse('ietf.meeting.views.edit_session', kwargs={'session_id': session.pk})
+        r = self.client.get(url)
+        self.assertContains(r, 'Edit session', status_code=200)
+        r = self.client.post(url, {
+            'name': 'this is a name',
+            'short': 'tian',
+            'purpose': 'coding',
+            'type': 'other',
+            'requested_duration': '3600',
+            'on_agenda': True,
+            'remote_instructions': 'Do this do that',
+            'attendees': '103',
+            'comments': 'So much to say',
+        })
+        self.assertNoFormPostErrors(r)
+        self.assertRedirects(r, urlreverse('ietf.meeting.views.edit_meeting_schedule',
+                                           kwargs={'num': session.meeting.number}))
+        session = Session.objects.get(pk=session.pk)  # refresh objects from DB
+        self.assertEqual(session.name, 'this is a name')
+        self.assertEqual(session.short, 'tian')
+        self.assertEqual(session.purpose_id, 'coding')
+        self.assertEqual(session.type_id, 'other')
+        self.assertEqual(session.requested_duration, datetime.timedelta(hours=1))
+        self.assertEqual(session.on_agenda, True)
+        self.assertEqual(session.remote_instructions, 'Do this do that')
+        self.assertEqual(session.attendees, 103)
+        self.assertEqual(session.comments, 'So much to say')
 
     def test_edit_timeslots(self):
         meeting = make_meeting_test_data()
diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py
index 7e42e1940..a051bd953 100644
--- a/ietf/meeting/urls.py
+++ b/ietf/meeting/urls.py
@@ -4,7 +4,7 @@ from django.conf.urls import include
 from django.views.generic import RedirectView
 from django.conf import settings
 
-from ietf.meeting import views, ajax, views_proceedings
+from ietf.meeting import views, views_proceedings
 from ietf.utils.urls import url
 
 safe_for_all_meeting_types = [
@@ -26,10 +26,7 @@ safe_for_all_meeting_types = [
 
 
 type_ietf_only_patterns = [
-    url(r'^agenda/%(owner)s/%(schedule_name)s/edit$' % settings.URL_REGEXPS,
-        RedirectView.as_view(pattern_name='ietf.meeting.views.edit_meeting_schedule', permanent=True),
-        name='ietf.meeting.views.edit_schedule'),
-    url(r'^agenda/%(owner)s/%(schedule_name)s/edit/$' % settings.URL_REGEXPS, views.edit_meeting_schedule),
+    url(r'^agenda/%(owner)s/%(schedule_name)s/edit/?$' % settings.URL_REGEXPS, views.edit_meeting_schedule),
     url(r'^agenda/%(owner)s/%(schedule_name)s/timeslots/$' % settings.URL_REGEXPS, views.edit_meeting_timeslots_and_misc_sessions),
     url(r'^agenda/%(owner)s/%(schedule_name)s/details$' % settings.URL_REGEXPS, views.edit_schedule_properties),
     url(r'^agenda/%(owner)s/%(schedule_name)s/delete$' % settings.URL_REGEXPS, views.delete_schedule),
@@ -40,10 +37,6 @@ type_ietf_only_patterns = [
     url(r'^agenda/%(owner)s/%(schedule_name)s/by-room/?$' % settings.URL_REGEXPS, views.agenda_by_room),
     url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/?$' % settings.URL_REGEXPS, views.agenda_by_type),
     url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/(?P<type>[a-z]+)$' % settings.URL_REGEXPS, views.agenda_by_type),
-    url(r'^agenda/%(owner)s/%(schedule_name)s/permissions$' % settings.URL_REGEXPS, ajax.schedule_permission_api),
-    url(r'^agenda/%(owner)s/%(schedule_name)s/session/(?P<assignment_id>\d+).json$' % settings.URL_REGEXPS, ajax.assignment_json),
-    url(r'^agenda/%(owner)s/%(schedule_name)s/sessions.json$' % settings.URL_REGEXPS,      ajax.assignments_json),
-    url(r'^agenda/%(owner)s/%(schedule_name)s.json$' % settings.URL_REGEXPS, ajax.schedule_infourl),
     url(r'^agenda/%(owner)s/%(schedule_name)s/new/$' % settings.URL_REGEXPS, views.new_meeting_schedule),
     url(r'^agenda/by-room$', views.agenda_by_room),
     url(r'^agenda/by-type$', views.agenda_by_type),
@@ -58,20 +51,8 @@ type_ietf_only_patterns = [
     url(r'^timeslot/new$',                       views.create_timeslot),
     url(r'^timeslot/(?P<slot_id>\d+)/edit$',     views.edit_timeslot),
     url(r'^timeslot/(?P<slot_id>\d+)/edittype$', views.edit_timeslot_type),
-    url(r'^rooms$',                              ajax.timeslot_roomsurl),
-    url(r'^room/(?P<roomid>\d+).json$',          ajax.timeslot_roomurl),
-    url(r'^timeslots$',                          ajax.timeslot_slotsurl),
-    url(r'^timeslots.json$',                     ajax.timeslot_slotsurl),
-    url(r'^timeslot/(?P<slotid>\d+).json$',      ajax.timeslot_sloturl),
-    url(r'^agendas$',                            ajax.schedule_infosurl),
-    url(r'^agendas.json$',                       ajax.schedule_infosurl),
     url(r'^agenda/(?P<acronym>[-a-z0-9]+)-drafts.pdf$', views.session_draft_pdf),
     url(r'^agenda/(?P<acronym>[-a-z0-9]+)-drafts.tgz$', views.session_draft_tarfile),
-    url(r'^sessions\.json$',                               ajax.sessions_json),
-    url(r'^session/(?P<sessionid>\d+).json',             ajax.session_json),
-    url(r'^session/(?P<sessionid>\d+)/constraints.json', ajax.session_constraints),
-    url(r'^constraint/(?P<constraintid>\d+).json',       ajax.constraint_json),
-    url(r'^json$',                               ajax.meeting_json),
 ]
 
 # This is a limited subset of the list above -- many of the views above won't work for interim meetings
@@ -88,7 +69,7 @@ type_ietf_only_patterns_id_optional = [
     url(r'^agenda(?P<ext>.csv)$', views.agenda),
     url(r'^agenda/edit$',
         RedirectView.as_view(pattern_name='ietf.meeting.views.edit_meeting_schedule', permanent=True),
-        name='ietf.meeting.views.edit_schedule'),
+        name='ietf.meeting.views.edit_meeting_schedule'),
     url(r'^agenda/edit/$', views.edit_meeting_schedule),
     url(r'^requests$', views.meeting_requests),
     url(r'^agenda/agenda\.ics$', views.agenda_ical),
diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py
index fcadf6bfb..72161a16b 100644
--- a/ietf/meeting/views.py
+++ b/ietf/meeting/views.py
@@ -59,11 +59,7 @@ from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPr
 from ietf.meeting.models import SessionStatusName, SchedulingEvent, SchedTimeSessAssignment, Room, TimeSlotTypeName
 from ietf.meeting.forms import ( CustomDurationField, SwapDaysForm, SwapTimeslotsForm,
                                  TimeSlotCreateForm, TimeSlotEditForm, SessionEditForm )
-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_assignments_from_schedule
-from ietf.meeting.helpers import get_modified_from_assignments
-from ietf.meeting.helpers import get_wg_list, find_ads_for_meeting
+from ietf.meeting.helpers import get_person_by_email, get_schedule_by_name
 from ietf.meeting.helpers import get_meeting, get_ietf_meeting, get_current_ietf_meeting_num
 from ietf.meeting.helpers import get_schedule, schedule_permissions
 from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_file
@@ -280,86 +276,6 @@ def materials_editable_groups(request, num=None):
     return render(request, "meeting/materials_editable_groups.html", {
         'meeting_num': meeting.number})
 
-def ascii_alphanumeric(string):
-    return re.match(r'^[a-zA-Z0-9]*$', string)
-
-class SaveAsForm(forms.Form):
-    savename = forms.CharField(max_length=16)
-
-@role_required('Area Director','Secretariat')
-def schedule_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.
-        messages.error("Error: No meeting information for meeting %s owner %s schedule %s available" % (num, owner, name)) # pylint: disable=no-value-for-parameter
-        return redirect(edit_schedule, num=num, owner=owner, name=name)
-
-    # authorization was enforced by the @group_require decorator above.
-
-    saveasform = SaveAsForm(request.POST)
-    if not saveasform.is_valid():
-        messages.info(request, "This name is not valid. Please choose another one.")
-        return redirect(edit_schedule, num=num, owner=owner, name=name)
-
-    savedname = saveasform.cleaned_data['savename']
-
-    if not ascii_alphanumeric(savedname):
-        messages.info(request, "This name contains illegal characters. Please choose another one.")
-        return redirect(edit_schedule, num=num, owner=owner, name=name)
-
-    # create the new schedule, and copy the assignments
-    try:
-        sched = meeting.schedule_set.get(name=savedname, owner=request.user.person)
-        if sched:
-            return redirect(edit_schedule, num=meeting.number, owner=sched.owner_email(), name=sched.name)
-        else:
-            messages.info(request, "Schedule creation failed. Please try again.")
-            return redirect(edit_schedule, num=num, owner=owner, name=name)
-
-    except Schedule.DoesNotExist:
-        pass
-
-    # must be done
-    newschedule = Schedule(name=savedname,
-                           owner=request.user.person,
-                           meeting=meeting,
-                           base=schedule.base,
-                           origin=schedule,
-                           visible=False,
-                           public=False)
-
-    newschedule.save()
-    if newschedule is None:
-        return HttpResponse(status=500)
-
-    # keep a mapping so that extendedfrom references can be chased.
-    mapping = {};
-    for ss in schedule.assignments.all():
-        # hack to copy the object, creating a new one
-        # just reset the key, and save it again.
-        oldid = ss.pk
-        ss.pk = None
-        ss.schedule=newschedule
-        ss.save()
-        mapping[oldid] = ss.pk
-        #print "Copying %u to %u" % (oldid, ss.pk)
-
-    # now fix up any extendedfrom references to new set.
-    for ss in newschedule.assignments.all():
-        if ss.extendedfrom is not None:
-            oldid = ss.extendedfrom.id
-            newid = mapping[oldid]
-            #print "Fixing %u to %u" % (oldid, newid)
-            ss.extendedfrom = newschedule.assignments.get(pk = newid)
-            ss.save()
-
-
-    # now redirect to this new schedule.
-    return redirect(edit_schedule, meeting.number, newschedule.owner_email(), newschedule.name)
-
 
 @role_required('Secretariat')
 def edit_timeslots(request, num=None):
@@ -1397,80 +1313,6 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name
     })
 
 
-##############################################################################
-#@role_required('Area Director','Secretariat')
-# disable the above security for now, check it below.
-@ensure_csrf_cookie
-def edit_schedule(request, num=None, owner=None, name=None):
-
-    if request.method == 'POST':
-        return schedule_create(request, num, owner, name)
-
-    user     = request.user
-    meeting  = get_meeting(num)
-    person   = get_person_by_email(owner)
-    if name is None:
-        schedule = meeting.schedule
-    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
-
-    rooms = meeting.room_set.filter(session_types__slug='regular').distinct().order_by("capacity")
-    saveas = SaveAsForm()
-    saveasurl=reverse(edit_schedule,
-                      args=[meeting.number, schedule.owner_email(), schedule.name])
-
-    can_see, can_edit,secretariat = schedule_permissions(meeting, schedule, user)
-
-    if not can_see:
-        return render(request, "meeting/private_schedule.html",
-                                             {"schedule":schedule,
-                                              "meeting": meeting,
-                                              "meeting_base_url":meeting_base_url,
-                                              "hide_menu": True
-                                          }, status=403, content_type="text/html")
-
-    assignments = get_all_assignments_from_schedule(schedule)
-
-    # get_modified_from needs the query set, not the list
-    modified = get_modified_from_assignments(assignments)
-
-    area_list = get_areas()
-    wg_name_list = get_wg_name_list(assignments)
-    wg_list = get_wg_list(wg_name_list)
-    ads = find_ads_for_meeting(meeting)
-    for ad in ads:
-        # set the default to avoid needing extra arguments in templates
-        # django 1.3+
-        ad.default_hostscheme = site_base_url
-
-    time_slices,date_slices = build_all_agenda_slices(meeting)
-
-    return render(request, "meeting/landscape_edit.html",
-                                         {"schedule":schedule,
-                                          "saveas": saveas,
-                                          "saveasurl": saveasurl,
-                                          "meeting_base_url": meeting_base_url,
-                                          "site_base_url": site_base_url,
-                                          "rooms":rooms,
-                                          "time_slices":time_slices,
-                                          "date_slices":date_slices,
-                                          "modified": modified,
-                                          "meeting":meeting,
-                                          "area_list": area_list,
-                                          "area_directors" : ads,
-                                          "wg_list": wg_list ,
-                                          "assignments": assignments,
-                                          "show_inline": set(["txt","htm","html"]),
-                                          "hide_menu": True,
-                                          "can_edit_properties": can_edit or secretariat,
-                                      })
-
-
 class SchedulePropertiesForm(forms.ModelForm):
     class Meta:
         model = Schedule
@@ -1504,7 +1346,7 @@ def edit_schedule_properties(request, num, owner, name):
            form.save()
            if request.GET.get('next'):
                return HttpResponseRedirect(request.GET.get('next'))
-           return redirect('ietf.meeting.views.edit_schedule', num=num, owner=owner, name=name)
+           return redirect('ietf.meeting.views.edit_meeting_schedule', num=num, owner=owner, name=name)
     else:
         form = SchedulePropertiesForm(meeting, instance=schedule)
 
diff --git a/ietf/person/ajax.py b/ietf/person/ajax.py
index ee4cbb0ea..e8a4f1699 100644
--- a/ietf/person/ajax.py
+++ b/ietf/person/ajax.py
@@ -6,14 +6,6 @@ from django.http import HttpResponse
 from ietf.ietfauth.utils import role_required
 from ietf.person.models import Person
 
-def person_json(request, personid):
-    person = get_object_or_404(Person, pk=personid)
-
-    return HttpResponse(json.dumps(person.json_dict(request.build_absolute_uri("/")),
-                                   sort_keys=True, indent=2),
-                        content_type="application/json")
-
-
 @role_required('Secretariat')
 def person_email_json(request, personid):
     person = get_object_or_404(Person, pk=personid)
diff --git a/ietf/person/models.py b/ietf/person/models.py
index 47eaeb659..381fee968 100644
--- a/ietf/person/models.py
+++ b/ietf/person/models.py
@@ -228,18 +228,6 @@ class Person(models.Model):
     def defurl(self):
         return urljoin(self.default_hostscheme,self.json_url())
 
-    def json_url(self):
-        return "/person/%s.json" % (self.id, )
-
-    # return info about the person
-    def json_dict(self, hostscheme):
-        ct1 = dict()
-        ct1['person_id'] = self.id
-        ct1['href']      = urljoin(hostscheme, self.json_url())
-        ct1['name']      = self.name
-        ct1['ascii']     = self.ascii
-        return ct1
-
     def available_api_endpoints(self):
         from ietf.ietfauth.utils import has_role
         return list(set([ (v, n) for (v, n, r) in PERSON_API_KEY_VALUES if r==None or has_role(self.user, r) ]))
diff --git a/ietf/person/urls.py b/ietf/person/urls.py
index ffcda989d..5c894922f 100644
--- a/ietf/person/urls.py
+++ b/ietf/person/urls.py
@@ -4,7 +4,6 @@ from ietf.utils.urls import url
 urlpatterns = [
     url(r'^merge/$', views.merge),
     url(r'^search/(?P<model_name>(person|email))/$', views.ajax_select2_search),
-    url(r'^(?P<personid>[a-z0-9]+).json$', ajax.person_json),
     url(r'^(?P<personid>[a-z0-9]+)/email.json$', ajax.person_email_json),
     url(r'^(?P<email_or_name>[^/]+)$', views.profile),
     url(r'^(?P<email_or_name>[^/]+)/photo/?$', views.photo),
diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py
index d5034eb25..7443e28f8 100644
--- a/ietf/secr/sreq/views.py
+++ b/ietf/secr/sreq/views.py
@@ -539,10 +539,6 @@ def edit(request, acronym, num=None):
                 # send notification
                 send_notification(group,meeting,login,form.cleaned_data,'update')
 
-            # nuke any cache that might be lingering around.
-            from ietf.meeting.helpers import session_constraint_expire
-            session_constraint_expire(request,session)
-
             messages.success(request, 'Session Request updated')
             return redirect('ietf.secr.sreq.views.view', acronym=acronym)
 
diff --git a/ietf/static/ietf/js/agenda/agenda_edit.js b/ietf/static/ietf/js/agenda/agenda_edit.js
deleted file mode 100644
index 81bc58692..000000000
--- a/ietf/static/ietf/js/agenda/agenda_edit.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
-*
-*  FILE: agenda_edit.js
-* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE.
-*
-*   www.credil.org: Project Orlando 2013
-*   Author: Justin Hornosty ( justin@credil.org )
-*           Michael Richardson <mcr@sandelman.ca>
-*
-*  Description:
-*      This is the main file for the agenda editing page.
-*      It contains the document read function that starts everything
-*      off, and uses functions and objects from agenda_*.js
-*
-*/
-
-
-
-
-//////////////-GLOBALS----////////////////////////////////////////
-
-// these need to be setup in landscape_edit's setup_slots() inline function:
-//var meeting_number = 0;   // is the meeting name.
-//var schedule_id    = 0;   // what is the schedule we are editing.
-//var schedule_name;        // what is the schedule we are editing.
-//var schedule_owner_href = '';  // who owns this schedule
-//var assignments_post_href;
-//var meeting_base_url;
-//var site_base_url;
-//var total_rooms = 0; // the number of rooms
-//var total_days = 0; // the number of days
-
-var is_secretariat = false;
-
-var agenda_globals;
-
-var area_directors = {};  // list of promises of area directors, index by href.
-
-var read_only = true;     // it is true until we learn otherwise.
-var days = [];
-var legend_status = {};   // agenda area colors.
-var load_conflicts = true;
-var duplicate_sessions = {};
-/********* colors ************************************/
-
-var dragging_color = "blue"; // color when draging events.
-var none_color = '';  // when we reset the color. I believe doing '' will force it back to the stylesheet value.
-var color_droppable_empty_slot = 'rgb(0, 102, 153)';
-
-// these are used for debugging only.
-var last_json_txt   = "";   // last txt from a json call.
-var last_json_reply = [];   // last parsed content
-
-var hidden_rooms = [];
-var hidden_days = [];
-
-/****************************************************/
-
-/////////////-END-GLOBALS-///////////////////////////////////////
-
-/* refactor this out into the html */
-$(document).ready(function() {
-    initStuff();
-
-   $("#close_ietf_menubar").click();
-
-});
-
-/* initStuff()
-   This is ran at page load and sets up the entire page.
-*/
-function initStuff(){
-    agenda_globals = new AgendaGlobals();
-    //agenda_globals.__debug_session_move = true;
-
-    log("initstuff() running...");
-    var directorpromises = [];
-
-    /* define a slot for unscheduled items */
-    var unassigned = new ScheduledSlot();
-    unassigned.make_unassigned();
-
-    setup_slots(directorpromises);
-    mark_area_directors(directorpromises);
-    log("setup_slots() ran");
-    droppable();
-    log("droppable() ran");
-
-    $.when.apply($,directorpromises).done(function() {
-        /* can not load events until area director info,
-           timeslots, sessions, and assignments
-           have been loaded
-        */
-        log("loading/linking objects");
-        load_events();
-        log("load_events() ran");
-        find_meeting_no_room();
-        calculate_name_select_box();
-        calculate_room_select_box();
-        listeners();
-        droppable();
-        duplicate_sessions = find_double_timeslots();
-        empty_info_table();
-        count_sessions();
-
-        if(load_conflicts) {
-            recalculate(null);
-        }
-    });
-
-    static_listeners();
-    log("listeners() ran");
-
-    start_spin();
-
-    read_only = true;
-    log("do read only check");
-    read_only_check();
-    stop_spin();
-
-    meeting_objs_length = Object.keys(agenda_globals.meeting_objs).length;
-
-    /* Comment this out for fast loading */
-    //load_conflicts = false;
-}
-
-var __READ_ONLY;
-function read_only_result(msg) {
-    __READ_ONLY = msg;
-    is_secretariat = msg.secretariat;
-
-    read_only = msg.read_only;
-    console.log("read only", read_only);
-
-    if(!read_only) {
-	$("#read_only").css("display", "none");
-    }
-
-    if(msg.save_perm) {
-        $(".agenda_save_box").css("display", "block");
-        if(read_only) {
-            $(".agenda_save_box").css("position", "fixed");
-            $(".agenda_save_box").css("top", "20px");
-            $(".agenda_save_box").css("right", "10px");
-            $(".agenda_save_box").css("bottom", "auto");
-            $(".agenda_save_box").css("border", "3px solid blue");
-            $(".agenda_save_box").css("z-index", "2000");
-        }
-    } else {
-        $(".agenda_save_box").html("please login to save");
-    }
-
-    schedule_owner_href = msg.owner_href;
-    // XX go fetch the owner and display it.
-    console.log("owner href:", schedule_owner_href);
-
-    $("#pageloaded").show();
-
-    listeners();
-    droppable();
-}
-
-function read_only_check() {
-    var read_only_url  = meeting_base_url + "/agenda/" + schedule_owner_email + "/" + schedule_name + "/permissions";
-    console.log("Loading readonly status from: ", read_only_url);
-    var read_only_load = $.ajax(read_only_url);
-
-    read_only_load.success(function(newobj, status, jqXHR) {
-        last_json_reply = newobj;
-        read_only_result(newobj);
-    });
-}
-
-function print_all_ss(objs){
-    console.log(objs)
-}
-
-
-/*
- * Local Variables:
- * c-basic-offset:4
- * End:
- */
-
diff --git a/ietf/static/ietf/js/agenda/agenda_helpers.js b/ietf/static/ietf/js/agenda/agenda_helpers.js
deleted file mode 100644
index 8cb36a0db..000000000
--- a/ietf/static/ietf/js/agenda/agenda_helpers.js
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
-*   agenda_helpers.js
-*
-* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE.
-*
-*   www.credil.org: Project Orlando 2013
-*   Author: Justin Hornosty ( justin@credil.org )
-*           Michael Richardson <mcr@sandelman.ca>
-*
-*   Should contain miscellaneous commonly used functions.
-*
-*
-*/
-
-/* do_work:
-   when we are waiting for something to either resolve to true, or another similar job
-   this function should achieve this.
-
-   result will be a function that when returns true will stop the work and then the callback
-   will be triggered.
-
-   ex:
-      global_x = 0
-      do_work(function(){ global_x++; return global_x > 100 }, function(){ console.log("resolved") })
-*/
-function do_work(result,callback){
-    setTimeout(function(){
-	if(!result()){
-	    setTimeout(arguments.callee,1);
-	}
-	else{
-	    callback();
-	}
-    });
-}
-
-
-function log(text){
-    console.log(text);
-}
-
-function print_all(){
-    console.log("all");
-    console.log(agenda_globals.meeting_objs.length);
-    for(var i=0; i<agenda_globals.meeting_objs.length; i++){
-		agenda_globals.meeting_objs[i].print_out();
-    }
-}
-
-function find_title(title){
-    $.each(agenda_globals.meeting_objs, function(key){
-	if (agenda_globals.meeting_objs[key].title == title) {
-	    console.log(agenda_globals.meeting_objs[key]);
-	}
-    });
-}
-function find_session_id(session_id){
-    $.each(agenda_globals.meeting_objs, function(key){
-	if (agenda_globals.meeting_objs[key].session_id == session_id) {
-	    console.log(agenda_globals.meeting_objs[key]);
-	}
-    });
-}
-
-function find_same_area(area){
-    var areas = []
-    area = area.toUpperCase();
-    $.each(agenda_globals.meeting_objs, function(index,obj){
-	if(obj.area == area){
-	    areas.push({id:index,slot_status_key:obj.slot_status_key})
-	    }
-    });
-    return areas
-}
-
-function style_empty_slots(){
-
-}
-
-var __debug_load_events = false;
-/* this pushes every event into the agendas */
-function load_events(){
-    var slot_id;
-
-    console.log("load events...");
-
-    /* first delete all html items that might have gotten saved if
-     * user save-as and went offline.
-     */
-    if(__debug_load_events) {
-        console.log("processing double slot status relations");
-    }
-
-    /* clear out all the timeslots */
-    $.each(agenda_globals.timeslot_bydomid, function(key) {
-        insert_cell(key, "", true);
-
-        var timeslot = agenda_globals.timeslot_bydomid[key];
-        slot_id = ("#"+key);
-
-	$(slot_id).addClass("agenda_slot_" + timeslot.roomtype);
-
-        if(timeslot.roomtype == "unavail") {
-            $(slot_id).removeClass("ui-droppable");
-            $(slot_id).removeClass("free_slot");
-            $(slot_id).addClass("agenda_slot_unavailable");
-        } else {
-            $(slot_id).removeClass("agenda_slot_unavailable");
-            $(slot_id).addClass("ui-droppable");
-        }
-    });
-
-    $.each(agenda_globals.slot_status, function(key) {
-        ssid_arr = agenda_globals.slot_status[key];
-
-	for(var q = 0; q<ssid_arr.length; q++){
-	    ssid = ssid_arr[q];
-
-            ssid.connect_to_timeslot_session();
-
-            // also see if the slots have any declared relationship, and take it forward as
-            // well as backwards.
-            if(ssid.extendedfrom_id != false) {
-                other = agenda_globals.slot_objs[ssid.extendedfrom_id];
-                if(__debug_load_events) {
-                    console.log("slot:",ssid.assignment_id, "extended from: ",key,ssid.extendedfrom_id); // ," is: ", other);
-                }
-                if(other != undefined) {
-                    ssid.extendedfrom = other;
-                    other.extendedto  = ssid;
-                } else {
-                    if(__debug_load_events) {
-                        console.log("extended from: ",ssid.extendedfrom_id," not found");
-                    }
-                }
-            }
-	}
-    });
-
-    // go through the slots again, and if one slot has been extended, then
-    // extend any other "sister" slots as well.
-    if(__debug_load_events) {
-        console.log("marking extended slots for slots with multiple sessions");
-    }
-    $.each(agenda_globals.slot_status, function(key) {
-        ssid_arr = agenda_globals.slot_status[key];
-
-        var extendedto = undefined;
-	for(var q = 0; q<ssid_arr.length; q++){
-	    ssid = ssid_arr[q];
-            if(extendedto == undefined &&
-               ssid.extendedto != undefined) {
-                if(__debug_load_events) {
-                    console.log("ssid",ssid.session_id,"extended 1");
-                }
-                extendedto = ssid.extendedto;
-            }
-        }
-	for(var q = 0; q<ssid_arr.length; q++){
-	    ssid = ssid_arr[q];
-            ssid.extendedto = extendedto;
-            if(__debug_load_events) {
-                console.log("ssid",ssid.session_id,"extended 2");
-            }
-        }
-    });
-
-    if(__debug_load_events) {
-        console.log("finding responsible ad");
-    }
-    $.each(agenda_globals.meeting_objs, function(key) {
-        session = agenda_globals.meeting_objs[key];
-        session.find_responsible_ad();
-    });
-
-    $.each(agenda_globals.slot_status, function(key) {
-        ssid_arr = agenda_globals.slot_status[key]
-	if(key == "sortable-list"){
-	    console.log("sortable list");
-	}else {
-	    for(var q = 0; q<ssid_arr.length; q++){
-	        ssid = ssid_arr[q];
-                slot_id = ("#"+ssid.domid());
-
-                if(__debug_load_events) {
-                    console.log("populating slot: ",slot_id,key);
-                }
-
-                if(ssid.timeslot.roomtype != "unavail") {
-                    session = agenda_globals.meeting_objs[ssid.session_id];
-                    if (session != null) {
-                        if(ssid.extendedto != undefined) {
-                            session.double_wide = true;
-                            session.slot2 = ssid.extendedto;
-                        }
-                        if(ssid.extendedfrom == undefined) {
-	       	            session.slot_status_key = key;
-                        }
-
-	                $(slot_id).removeClass('free_slot');
-
-                        if(ssid.extendedfrom == undefined) {
-                            if(__debug_load_events) {
-                                console.log("  with session", session.title);
-                            }
-
-                            session.populate_event(key);
-                        }
-                        session.placed(ssid.timeslot, false, ssid);
-                    } else {
-	                $(slot_id).addClass('free_slot');
-                    }
-                }
-            }
-	}
-    });
-
-    $.each(agenda_globals.meeting_objs, function(key) {
-        session = agenda_globals.meeting_objs[key];
-
-	// note in the group, what the set of column classes is.
-	// this is an array, as the group might have multiple
-	// sessions!
-	group = session.group;
-        if(group == undefined) {
-            console.log("session: ", session.title, "has no group_href:", session.group_href);
-        } else {
-            group.add_column_classes(session.column_class_list);
-	    group.add_session(session);
-        }
-    });
-
-}
-
-function check_free(inp){
-    var empty = false;
-    slot = agenda_globals.timeslot_bydomid[inp.id];
-    if(slot == null){
-        //console.log("\t from check_free, slot is null?", inp,inp.id, agenda_globals.slot_status[inp.id]);
-	return false;
-    }
-    if (slot.empty == false) {
-	return false;
-    }
-    return true;
-}
-
-/* clears any background highlight colors of scheduled sessions */
-function clear_highlight(inp_arr){ // @args: array from slot_status{}
-    if(inp_arr == null){
-	return false;
-    }
-    for(var i =0; i<inp_arr.length; i++){
-	$("#session_"+inp_arr[i].session_id).removeClass('free_slot');
-	$("#session_"+inp_arr[i].session_id).css('background-color','');
-    }
-    return true;
-
-}
-
-/* based on any meeting object, it finds any other objects inside the same timeslot. */
-function find_friends(inp){
-    var ts = $(inp).parent().attr('id');
-    var ss_arr = agenda_globals.slot_status[ts];
-    if (ss_arr != null){
-	return ss_arr;
-    }
-    else{
-	//console.log("find_friends("+inp+") did not find anything");
-	return null;
-    }
-}
-
-
-function json_to_id(j){
-    return (j.room()+"_"+j.date()+"_"+j.time());
-}
-
-function id_to_json(id){
-    if(id != null){
-	var split = id.split('_');
-	return {"room":split[0],"date":split[1],"time":split[2]}
-    }
-    else{
-	return null;
-    }
-}
-
-
-/* returns a the html for a row in a table
-   as: <tr><td>title</td><td>data</td></tr>
-*/
-function gen_tr_td(title,data){
-    return "<tr><td>"+title+"</td><td>"+data+"</td></tr>";
-}
-
-/* Mainly for the case where we didn't get any data back from the server */
-function empty_info_table(){
-    $("#info_grp").html(name_select_html);
-    $("#info_name").html("");
-    $("#info_area").html("");
-    $("#info_duration").html("");
-
-    $(".agenda_selected_buttons").attr('disabled',true);
-    $(".agenda_double_slot").addClass("button_disabled");
-    $(".agenda_double_slot").removeClass("button_enabled");
-
-    if(!read_only) {
-        $("#info_location").html(generate_select_box()+"<button id='info_location_set'>Set</button>");
-        $("#info_location_select").val("");
-        $("#info_location_select").val($("#info_location_select_option_"+current_timeslot_id).val());
-    }
-    $("#info_responsible").html("");
-    $("#info_requestedby").html("");
-    $("#agenda_requested_features").html("");
-
-    /* need to reset listeners, because we just changed the HTML */
-    listeners();
-}
-
-
-var temp_1;
-/* creates the 'info' table that is located on the right side.
-   takes in a json.
-*/
-
-function compare_timeslot(a,b) {
-    //console.log("day: a,b", a.day, b.day);
-
-    // sometimes (a.day==b.say)==false and (a.day===b.day)==false,
-    // for days that appear identical, but built from different strings,
-    // yet (a.day-b.day)==0.
-    if((a.day - b.day) == 0) {
-        //console.log("time: a,b", a.starttime, b.starttime);
-        if(a.starttime == b.starttime) {
-            //console.log("room: a,b", a.room, b.room, a.room < b.room);
-            if(a.room > b.room) {
-                return 1;
-            } else {
-                return -1;
-            }
-        };
-        if(a.starttime > b.starttime) {
-            return 1;
-        } else {
-            return -1;
-        }
-    }
-    if(a.day > b.day) {
-        return 1;
-    } else {
-        return -1;
-    }
-}
-
-var room_select_html = "";
-function calculate_room_select_box() {
-    var html = "<select id='info_location_select'>";
-    var mobj_array = [];
-
-    $.each(agenda_globals.timeslot_byid, function(key, value){
-        mobj_array.push(value)
-    });
-
-    var sorted = mobj_array.sort(compare_timeslot);
-    var lastone_id = undefined;
-
-    $.each(sorted, function(index, value) {
-        // this check removes duplicates from the list, if there are any.
-        if(value.roomtype == "break" || value.roomtype=="reg") {
-            return;
-        }
-        if(value.timeslot_id == lastone_id) {
-            return; // from subfunction.
-        }
-        //console.log("room_select_html", index, value, value.short_string);
-        html=html+"<option value='"+value.timeslot_id;
-        html=html+"' id='info_location_select_option_";
-        html=html+value.timeslot_id+"'>";
-        html=html+value.short_string;
-        if(value.roomtype != "regular") {
-            html = html+ "(" + value.roomtype + ")";
-        }
-        html=html+"</option>";
-        lastone_id = value.timeslot_id;
-    });
-    html = html+"</select>";
-    room_select_html = html;
-    return room_select_html;
-}
-
-var name_select_html = undefined;
-var temp_sorted = null;
-function calculate_name_select_box(){
-    var html = "<select id='info_name_select'>";
-    var mobj_array = [];
-    var mobj_array2;
-    $.each(agenda_globals.meeting_objs, function(key, value){ mobj_array.push(value) });
-    mobj_array2 = mobj_array.sort(function(a,b) { return a.title.localeCompare(b.title); });
-
-    var mlen = mobj_array.length;
-    console.log("calculate name_select box with",mlen,"objects");
-    for(var i = 0; i < mlen; i++){
-	//console.log("select box mobj["+i+"]="+mobj_array[i]);
-	// html=html+"<option value='"+mobj_array[i].slot_status_key;
-	html=html+"<option value='"+mobj_array[i].session_id;
-        html=html+"' id='info_name_select_option_";
-	ts_id = "err";
-	//console.log(mobj_array[i].session_id);
-	try{
-	    ts_id = mobj_array[i].session_id;
-	}catch(err){
-	    console.log(err); // bucket list items.
-
-	}
-        html=html+ts_id+"'>";
-
-
-	try{
-	    html=html+mobj_array[i].title; // + " (" + mobj_array[i].description + ")";
-	} catch(err) {
-	    html=html+"ERRROR!!!";
-	}
-        html=html+"</option>";
-    }
-
-    html = html+"</select>";
-    name_select_html = html;
-    return html;
-}
-
-
-
-function generate_select_box(){
-    if(!room_select_html) {
-        calculate_name_select_box();
-    }
-    return room_select_html;
-}
-
-
-
-
-function insert_cell(js_room_id, text, replace){
-    slot_id = ("#"+js_room_id);
-    try{
-	var found;
-        if(replace) {
-	    found = $(slot_id).html(text);
-        } else {
-            found = $(slot_id).append($(text));
-
-        }
-        $(slot_id).css('background','');
-	$(slot_id).removeClass('free_slot');
-        if(found.length == 0){
-            // do something here, if length was zero... then?
-        }
-
-    }
-    catch(err){
-	log("error");
-	log(err);
-    }
-}
-
-
-function find_meeting_no_room(){
-    $.each(agenda_globals.meeting_objs, function(key){
-	if(agenda_globals.meeting_objs[key].slot_status_key == null) {
-	    session = agenda_globals.meeting_objs[key]
-	    session.slot_status_key = null;
-	    session.populate_event(bucketlist_id);
-	}
-    })
-}
-
-
-/* in some cases we have sessions that span over two timeslots.
-   so we end up with two slot_status pointing to the same meeting_obj.
-   this this occures when someone requests a session that is extra long
-   which will then fill up the next timeslot.
-
-   this functions finds those cases.
-
-   returns a json{ 'ts': arr[time_slot_ids] }
-
-*/
-function find_double_timeslots(){
-    var duplicate = {};
-
-    $.each(agenda_globals.slot_status, function(key){
-	for(var i =0; i<agenda_globals.slot_status[key].length; i++){
-	    // goes threw all the slots
-	    var ss_id = agenda_globals.slot_status[key][i].session_id;
-	    if(duplicate[ss_id]){
-		duplicate[ss_id]['count']++;
-		duplicate[ss_id]['ts'].push(key);
-	    }
-	    else{
-		duplicate[ss_id] = {'count': 1, 'ts':[key]};
-
-	    }
-	}
-    });
-
-    var dup = {};
-    // console.log(duplicate);
-    $.each(duplicate, function(key){
-	if(duplicate[key]['count'] > 1){
-	    dup[key] = duplicate[key]['ts'];
-
-	}
-    });
-    return dup;
-}
-
-
-var child = null;
-/* removes a duplicate timeslot. completely. it's gone. */
-function remove_duplicate(timeslot_id, ss_id){
-    children = $("#"+timeslot_id).children();
-    child = children;
-    for(var i = 0; i< children.length; i++){ // loop to
-	if($(children[i]).attr('session_id') == ss_id) { // make sure we only remove duplicate.
-	    try{
-		$(children[i]).remove();
-	    }catch(exception){
-		console.log("exception from remove_duplicate",exception);
-	    }
-	}
-    }
-
-}
-
-
-
-function auto_remove(){
-    dup = find_double_timeslots();
-    $.each(dup, function(key){
-	remove_duplicate(dup[key][1], key);
-    })
-}
-
-
-
-/* for the spinnner */
-
-/* spinner code from:
-       http://fgnass.github.com/spin.js/
-
-       ex: $("#spinner").spin()      < start the spin
-           $("#spinner").spin(false) < stop the spin
-
-       http://gist.github.com/itsflorida   < jquery functionality.
-
-       lines: 30,            // The number of lines to draw
-       length: 7,            // The length of each line
-       width: 1,             // The line thickness
-       radius: 20,           // The radius of the inner circle
-       corners: 1,           // Corner roundness (0..1)
-       rotate: 0,            // The rotation offset
-       color: '#000',        // #rgb or #rrggbb
-       speed: 1,             // Rounds per second
-       trail: 60,            // Afterglow percentage
-       shadow: false,        // Whether to render a shadow
-       hwaccel: true,        // Whether to use hardware acceleration
-       className: 'spinner', // The CSS class to assign to the spinner
-       zIndex: 2e9,          // The zindex (defaults to 2000000000)
-       top: 'auto',          // Top position relative to parent in px
-       left: 'auto'          // Left position relative to parent in px
-
-*/
-
-(function($) {
-    $.fn.spin = function(opts, color) {
-        if (Spinner) {
-           return this.each(function() {
-               var $this = $(this),
-               data = $this.data();
-
-               if (data.spinner) {
-                   data.spinner.stop();
-                   delete data.spinner;
-               }
-               if (opts !== false) {
-                   if (typeof opts === "string") {
-                       if (opts in presets) {
-                           opts = presets[opts];
-                       } else {
-                           opts = {};
-                       }
-                       if (color) {
-                           opts.color = color;
-                       }
-                   }
-                   data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
-               }
-           });
-       } else {
-           throw "Spinner class not available.";
-       }
-    };
-})(jQuery);
-
-
-function start_spin(opts){
-//spinner
-    // $("#schedule_name").hide();
-    $("#spinner").show();
-    $("#spinner").spin({lines:16, radius:8, length:16, width:4});
-    $("#pageloaded").hide();
-}
-function stop_spin(){
-//spinner
-    $("#schedule_name").show();
-    $("#spinner").hide();
-    $("#spinner").spin(false);
-    $("#pageloaded").show();
-}
-
-/*
- * Local Variables:
- * c-basic-offset:4
- * End:
- */
diff --git a/ietf/static/ietf/js/agenda/agenda_listeners.js b/ietf/static/ietf/js/agenda/agenda_listeners.js
deleted file mode 100644
index 3ee70b5de..000000000
--- a/ietf/static/ietf/js/agenda/agenda_listeners.js
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
-*   agenda_listeners.js
-*
-* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE.
-*
-*   www.credil.org: Project Orlando 2013
-*   Author: Justin Hornosty ( justin@credil.org )
-*           Michael Richardson <mcr@sandelman.ca>
-*
-*   This file should contain functions relating to
-*   jquery ui droppable ( http://jqueryui.com/droppable/ )
-*   and other interactions.
-*
-*/
-
-var bucketlist_id = "sortable-list" // for if/when the id for bucket list changes.
-
-function resize_listeners() {
-    for(i = 0; i<days.length;i++){
-        $("#resize-"+days[i]+"-spacer").resizable({maxHeight:10,
-						   handles: "e",
-						   minWidth:2,
-
-						  });
-
-    }
-
-    $("#session-info").resizable({handles: "s",
-				  minWidth:"100%",
-				  containment: "parent"
-				 });
-
-}
-
-
-
-/* this function needs to be renamed...
-   it should only deal with listeners who need to be unbound prior to rebinding.
-*/
-function listeners(){
-    //$(".agenda_slot td").not(".meeting_event ui-draggable").unbind('click');
-    //$(".agenda_slot td").not(".meeting_event ui-draggable").click(function(event){ console.log("#meetings clicked"); console.log(event); });
-    $("#meetings").unbind('click');
-    $("#meetings").click(all_click);
-
-    // If we don't unbind it, things end up getting stacked, and tons of ajax things are sent.
-    $('.meeting_event').unbind('click');
-    $('.meeting_event').click(meeting_event_click);
-
-    $('#info_location_select').unbind('change');
-    $('#info_location_select').change(info_location_select_change);
-
-    $('#info_location_set').unbind('click');
-    $('#info_location_set').click(info_location_select_set);
-
-    $('#info_name_select').unbind('change');
-    $('#info_name_select').change(info_name_select_change);
-
-    $('.color_checkboxes').unbind('click');
-    $('.color_checkboxes').click(color_legend_click);
-
-    resize_listeners()
-
-    $('#find_free').unbind('click');
-    $('#find_free').click(function(event){ find_free(); });
-
-    $('#double_slot').unbind('click');
-
-    /* hiding rooms */
-    $(".close_room").unbind('click');
-    $(".close_room").click(close_room)
-
-    /* hiding days */
-    $(".close_day").unbind('click');
-    $(".close_day").click(close_day);
-
-    $("#show_all_area").unbind('click');
-    $("#show_all_area").click(show_all_area);
-
-    $("#show_all_button").unbind('click');
-    $("#show_all_button").click(show_all);
-
-    resize_th();
-}
-
-var __debug_movement_warnings = false;
-var __debug_toggle_result;
-var __debug_toggle_parameters;
-function toggle_dialog(parameters) {
-    var result = "null";
-    var message = "";
-    if(parameters.slot_occupied==true && parameters.too_small==false) {
-        dialogid = $( "#dialog-confirm-two" );
-    }
-    else if(parameters.slot_occupied==false && parameters.too_small==true) {
-        dialogid = $( "#dialog-confirm-toosmall" );
-    }
-    else if(parameters.too_small==true && parameters.slot_occupied==true){
-        dialogid = $( "#dialog-confirm-twotoosmall" );
-    }
-    else{
-	console.log("error, from toggle_dialog:", parameters);
-	return;
-    }
-
-    if(__debug_movement_warnings) {
-        console.log("toggle_dialog param", parameters, "message", message);
-        __debug_toggle_parameters = parameters;
-    }
-
-    dialogid.dialog({
-	resizable: true,
-	modal: true,
-	buttons: {
-            "Yes": function() {
-		$( this ).dialog( "close" );
-		__debug_toggle_result = "yes";
-                parameters.session.double_wide = false;
-                parameters.same_timeslot = true;
-		move_slot(parameters);
-            },
-	    //"Swap Slots": function(){
-	    //	$( this ).dialog( "close" );
-	    //	result = "swap";
-	    //},
-            Cancel: function() {
-		$( this ).dialog( "close" );
-		__debug_toggle_result = "cancel"
-            }
-	}
-    });
-
-    return __debug_toggle_result;
-}
-
-function resize_th(){
-/* with the hovering rooms the sizes get messed up
-   this function looks at the tr's height and resizes the room's height */
-    $.each($(".vert_time"), function(k,v){
-        $(v).offset({ left: 2});
-	$(v).height($("#"+v.parentElement.id).height()-2); /* -2 so the grid remains */
-    })
-}
-
-
-function clear_all_selections() {
-    $(".same_group").removeClass("same_group");
-    $(".selected_group").removeClass("selected_group");
-    $(".selected_slot").removeClass("selected_slot");
-}
-
-function all_click(event){
-    var all_classes = $(event.srcElement).attr('class');
-    var classes = [];
-    if(all_classes != undefined) {
-            classes = all_classes.split(' ');
-    }
-    // console.log("all_click:", classes, classes.indexOf('meeting_obj'), meeting_clicked);
-    if(!meeting_clicked && classes!=undefined && classes.indexOf('meeting_obj') < 0){
-        // console.log("32 show_all");
-        clear_all_selections();
-        clear_conflict_classes();   // remove the display showing the conflict classes.
-        last_session = null;
-        last_item    = null;
-    }
-    meeting_clicked = false;
-    // console.log(this);
-    // console.log($(this));
-}
-
-/************ click functions *********************************************************************/
-function update_room_count() {
-   $("#hidden_rooms").html((hidden_rooms.length.toString()+"/"+total_rooms.toString()));
-}
-
-function close_room(event){
-    var close_room = $(event.target).attr('id');
-    close_room =  close_room.substr(6);
-    //console.log("close_room",close_room);
-    $("#"+close_room).hide("fast");
-    hidden_rooms.push("#"+close_room);
-    update_room_count();
-}
-
-function show_hidden_rooms(event){
-    $.each(hidden_rooms, function(index,room){
-	$(room).show("fast");
-    });
-    hidden_rooms = [];
-    update_room_count();
-}
-
-function update_day_count() {
-    $("#hidden_days").html(hidden_days.length.toString()+"/"+total_days.toString());
-}
-
-function close_day(event){
-    var close_day = $(event.target).attr('id');
-    close_day = close_day.substr(6);
-    close_day = ".day_"+close_day;
-    $(close_day).hide("slow");
-    hidden_days.push(close_day);
-    update_day_count();
-}
-
-function show_all(){
-    show_hidden_days();
-    show_hidden_rooms();
-}
-
-function show_hidden_days(event){
-    $.each(hidden_days, function(index,room){
-	$(room).show("fast");
-    });
-    hidden_days = [];
-    update_day_count();
-}
-
-function show_all_area(event){
-    var areas = find_same_area($("#info_area").children().text());
-    //console.log("show all area",areas);
-    $.each(areas, function(index,obj){
-
-	var selector = $("#"+obj.slot_status_key);
-	if(slot_item_hidden(selector) ){
-	    $("#"+obj.slot_status_key).effect("highlight", {color:"lightcoral"}, 2000);
-	}
-    });
-}
-
-function show_all_session(event){
-    var session_class = "." + last_session.short_name;
-
-    $(session_class).parent().parent().parent().effect("highlight", {color:"lightcoral"}, 5000);
-}
-
-/************ END click functions *********************************************************************/
-
-function slot_item_hidden(selector){
-// checking if the thing we will visually display is hidden. (performing effects will break the previous hide)
-    var show = true;
-
-    $.each(hidden_days, function(index,value){
-	if(selector.hasClass(value.substr(1))){
-	    show=false;
-	    return show;
-	}
-    });
-    return show;
-}
-
-
-
-
-function find_empty_slot(session) {
-    var free_slots = [];
-
-    $.each(agenda_globals.timeslot_byid, function(id, slot) {
-        if(slot.empty && !slot.unscheduled_box) {
-	    free_slots.push(slot);
-	}
-    });
-
-    //console.log("free_slot list", free_slots);
-    var target_capacity = session.attendees;
-
-    // array of slots that have a capacity higher than the session.
-    var perfect_slots = [];
-
-    if(free_slots.length > 0){
-	for(var i = 0; i< free_slots.length; i++){
-	    if(free_slots[i].capacity  >= target_capacity) {
-		perfect_slots.push(free_slots[i]);
-	    }
-	}
-	if(perfect_slots.length > 0){
-	    return perfect_slots[0];
-	}
-	else{
-	    return free_slots[0]; // just return the first one.
-	}
-    }
-    else{
-	return null;
-    }
-}
-
-function extend_slot(event) {
-    // event is just the button push, ignore it.
-
-    session  = last_session;
-
-    console.log("session", session.title, "sslot:", current_scheduledslot.assignment_id);
-
-    /* bind current_timeslot into this function and continuations */
-    var slot = current_timeslot;
-
-    // determine if this slot can be extended.
-    if(current_timeslot.can_extend_right()) {
-        $("#can-extend-dialog").html("Extend "+session.title+" to slot "+slot.following_timeslot.domid);
-        $("#can-extend-dialog").dialog({
-	    resizable: true,
-	    modal: true,
-            dialogClass: "extend-dialog",
-	    buttons: {
-                "Yes": {
-                    click: function() {
-                        // need to create new assignment
-                        var new_ss = make_ss({ "session_id" : session.session_id,
-                                               "timeslot_id": slot.following_timeslot.timeslot_id,
-                                               "extendedfrom_id" : current_scheduledslot.assignment_id});
-                        // make_ss also adds to slot_status.
-                        new_ss.saveit();
-
-                        slot.extendedto = slot.following_timeslot;
-                        slot.extendedto.extendedfrom = slot;
-                        session.double_wide = true;
-                        session.repopulate_event(slot.domid);
-
-                        droppable();
-                        listeners();
-		        $( this ).dialog( "close" );
-
-                        // may have caused some new conflicts!!!!
-                        recalculate_conflicts_for_session(session,
-                                                          [slot.column_class],
-                                                          [slot.column_class, slot.extendedto.column_class]);
-                    },
-                    text: "Yes",
-                    id: "extend-yes"},
-                "Cancel": {
-                    click: function() {
-		        $( this ).dialog( "close" );
-		        result = "cancel"
-                    },
-                    text: "Cancel",
-                    id: "extend-cancel"},
-	    }
-        });
-    } else {
-        $( "#can-not-extend-dialog" ).dialog();
-    }
-}
-
-function find_free(){
-    if(last_session) {
-        var empty_slot = find_empty_slot(last_session);
-        if(empty_slot != null){
-            var domthing = $("#"+empty_slot.domid);
-	    domthing.effect("highlight", {},3000);
-	    if(current_item != null){
-	        $(current_item).addClass('ui-effects-transfer');
-	        $(current_item).effect("transfer", {to: domthing }, 1000);
-	    }
-	    $(current_item).removeClass('ui-effects-transfer');
-        }
-    }
-}
-
-
-function expand_spacer(target) {
-    var current_width = $(target).css('min-width');
-    current_width = current_width.substr(0,current_width.search("px"));
-    current_width = parseInt(current_width) + 20;
-    $(target).css('min-width',current_width);
-    $(target).css('width',current_width);
-
-}
-
-function sort_by_alphaname(a,b) {
-    am = agenda_globals.meeting_objs[$(a).attr('session_id')]
-    bm = agenda_globals.meeting_objs[$(b).attr('session_id')]
-    if(am.title < bm.title) {
-        return -1;
-    } else {
-        return 1;
-    }
-}
-function sort_by_area(a,b) {
-    am = agenda_globals.meeting_objs[$(a).attr('session_id')]
-    bm = agenda_globals.meeting_objs[$(b).attr('session_id')]
-    if(am.area < bm.area) {
-        return -1;
-    } else {
-        return 1;
-    }
-}
-function sort_by_duration(a,b) {
-    am = agenda_globals.meeting_objs[$(a).attr('session_id')]
-    bm = agenda_globals.meeting_objs[$(b).attr('session_id')]
-    if(am.requested_duration < bm.requested_duration) {
-        // sort duration biggest to smallest.
-        return 1;
-    } else if(am.requested_duration == bm.requested_duration &&
-              am.title    < bm.title) {
-        return 1;
-    } else {
-        return -1;
-    }
-}
-function sort_by_specialrequest(a,b) {
-    am = agenda_globals.meeting_objs[$(a).attr('session_id')]
-    bm = agenda_globals.meeting_objs[$(b).attr('session_id')]
-    if(am.special_request == '*' && bm.special_request == '') {
-        return -1;
-    } else if(am.special_request == '' && bm.special_request == '*') {
-        return 1;
-    } else if(am.title < bm.title) {
-        return -1;
-    } else {
-        return 1;
-    }
-}
-
-function sort_unassigned() {
-    $('#'+bucketlist_id+" div.meeting_box_container").sort(unassigned_sort_function).appendTo('#'+bucketlist_id);
-}
-
-var unassigned_sort_function = sort_by_alphaname;
-function unassigned_sort_change(){
-    var last_sort_method = unassigned_sort_function;
-    var sort_method= $("#unassigned_sort_button").attr('value');
-
-    if(sort_method == "alphaname") {
-        unassigned_sort_function = sort_by_alphaname;
-    } else if(sort_method == "area") {
-        unassigned_sort_function = sort_by_area;
-    } else if(sort_method == "duration") {
-        unassigned_sort_function = sort_by_duration;
-    } else if(sort_method == "special") {
-        unassigned_sort_function = sort_by_specialrequest;
-    } else {
-        unassigned_sort_function = sort_by_alphaname;
-    }
-
-    if(unassigned_sort_function != last_sort_method) {
-        sort_unassigned();
-    }
-}
-
-
-/* the functionality of these listeners will never change so they do not need to be run twice  */
-function static_listeners(){
-    $('#close_ietf_menubar').click(hide_ietf_menu_bar);
-
-    $("#show_hidden_days").unbind('click');
-    $("#show_hidden_days").click(show_hidden_days);
-    $("#show_hidden_rooms").unbind('click');
-    $("#show_hidden_rooms").click(show_hidden_rooms);
-
-    $('#unassigned_sort_button').unbind('change');
-    $('#unassigned_sort_button').change(unassigned_sort_change);
-    $('#unassigned_sort_button').css('display','block');
-    $("#unassigned_alpha").attr('selected',true);
-    sort_unassigned();
-}
-
-// recalculate all conflicts from scratch
-function recalculate(event) {
-    start_spin();
-    console.log("loading all conflicts");
-
-    var promise = get_all_conflicts();
-    promise.done(function() {
-        stop_spin();
-        console.log("showing all conflicts");
-        show_all_conflicts();
-    });
-}
-
-function color_legend_click(event){
-    var clicked = $(event.target).attr('id');
-    if(legend_status[clicked]){
-	legend_status[clicked] = false;
-    }
-    else{
-	legend_status[clicked] = true;
-    }
-    set_transparent();
-}
-
-var conflict_status = {};
-
-function conflict_click(event){
-    var clicked = $(event.target).attr('id');
-    var constraint = find_conflict(clicked);
-    //console.log("7 fill", clicked, conflict_status[clicked]);
-    if(conflict_status[clicked] == true){
-        //console.log("8 fill", constraint.href);
-	conflict_status[clicked] = false;
-	constraint.checked = "checked";
-    }
-    else{
-        //console.log("9 fill", constraint.href);
-	conflict_status[clicked] = true;
-	constraint.show_conflict_view();
-    }
-}
-
-function set_transparent(){
-    $.each(legend_status, function(k){
-	if(legend_status[k] != true){
-	    $("."+k+"-scheme.meeting_obj").parent().parent().parent().draggable("option","disabled",true);
-	}else{
-	    $("."+k+"-scheme.meeting_obj").parent().parent().parent().draggable("option","disabled",false);
-	}
-
-    });
-}
-
-var __debug_meeting_click = false;
-var clicked_event;
-var __DEBUG__SESSION_OBJ;
-var __DEBUG__SLOT_OBJ;
-var __debug_click_slot_id;
-var __debug_click_container;
-
-// current_item is the domid that was clicked
-// last_session is the session active.
-var current_item = null;
-var current_timeslot = null;
-var current_scheduledslot = null;
-var current_timeslot_id = null;  // global used by empty_info_table to move picker.
-var meeting_clicked  = false;
-function meeting_event_click(event){
-    //hide_all_conflicts();
-    try{
-	clear_highlight(find_friends(current_item));
-    }catch(err){ }
-
-    if(__debug_meeting_click) {
-        console.log("1 meeting_click:", event);
-    }
-
-    // keep event from going up the chain.
-    event.preventDefault();
-    meeting_clicked = true;
-
-    var slot_id = $(event.target).closest('.agenda_slot').attr('id');
-    var container  = $(event.target).closest('.meeting_box_container');
-    __debug_click_slot_id   = slot_id;
-    __debug_click_container = container;
-
-    if(container == undefined) {
-	return;
-    }
-
-    var session_id = container.attr('session_id');
-    var session = agenda_globals.meeting_objs[session_id];
-
-    select_session(session);
-    __DEBUG__SS_OBJ   = current_scheduledslot;
-    __DEBUG__SLOT_OBJ = current_timeslot;
-    __DEBUG__SESSION_OBJ = session;
-}
-
-function select_session(session) {
-    if(last_session != null) {
-        last_session.unselectit();
-    }
-    last_session = session;
-
-    empty_info_table();
-    current_item = session.element();
-
-    /* clear set ot conflict views */
-    clear_conflict_classes();
-    conflict_classes = {};
-
-    current_timeslot      = session.slot;
-    if(current_timeslot) {
-        current_timeslot_id   = current_timeslot.timeslot_id;
-    }
-    current_scheduledslot = session.assignment;
-    if(__debug_meeting_click) {
-        console.log("2 meeting_click:", current_timeslot, session);
-    }
-
-    fill_in_session_info(session, true, session.slot);
-}
-
-var last_item = null; // used during location change we make the background color
-// of the timeslot highlight because it is being set into that slot.
-function info_location_select_change(){
-    if(last_item != null){
-	$(last_item).removeClass("selected_slot");
-    }
-
-    last_item = '#'+$('#info_location_select').val();
-    $(last_item).addClass("selected_slot");
-}
-
-// called when the "Set" button is called, needs to perform a move, as if
-// it was dragged and dropped.
-function info_location_select_set() {
-    // figure out where the item was, and where it is going to.
-    var session = last_session;
-    var from_slot = session.slot;
-
-    var id = $('#info_location_select').val();
-    var to_slot = agenda_globals.timeslot_byid[id];
-
-    console.log("moved by select box from", from_slot, "to", to_slot);
-
-    move_thing({ "session": session,
-                 "to_slot_id": to_slot.domid,
-                 "to_slot":    to_slot,
-                 "dom_obj":      "#" + to_slot.domid,
-                 "from_slot_id": from_slot.domid,
-                 "from_slot":    from_slot});
-}
-
-function move_thing(parameters) {
-    // hasn't moved don't do anything
-    if(parameters.from_slot_id == parameters.to_slot_id){
-        $(parameters.dom_obj).removeClass('highlight_free_slot');
-	return;
-    }
-
-    parameters.too_small = false;
-    parameters.slot_occupied = false;
-
-    var room_capacity = parameters.to_slot.capacity;
-
-    if(parameters.session.session_attendees > room_capacity){
-	parameters.too_small = true;
-    }
-
-    parameters.bucket_list = (parameters.to_slot_id == bucketlist_id);
-    if(!parameters.bucket_list && !parameters.to_slot.empty){
-	parameters.slot_occupied = true
-    }
-
-    if(parameters.too_small || parameters.slot_occupied){
-	toggle_dialog(parameters);
-	return
-    }
-
-    clear_conflict_classes();
-    // clear double wide setting for now.
-    // (could return if necessary)
-    parameters.session.double_wide = false;
-
-    move_slot(parameters);
-}
-
-
-
-var last_session = null;
-var last_name_item = null;
-function info_name_select_change(){
-    if(last_session != null) {
-        console.log("unselecting:",last_session.title);
-	/* clear set ot conflict views */
-	clear_conflict_classes();
-	conflict_classes = {};
-        last_session.unselectit();
-    }
-    $(".same_group").removeClass("same_group");
-    $(".selected_group").removeClass("selected_group");
-    $(".selected_slot").removeClass("selected_slot");
-
-    if(last_item != null) {
-        $(last_item).removeClass("selected_slot");
-    }
-
-    var slot_id    = $('#info_name_select').val();
-    last_name_item = '#'+slot_id;
-    console.log("selecting group", slot_id);
-
-    var ssk = agenda_globals.meeting_objs[slot_id].slot_status_key;
-    // ssk is null when item is in bucket list.
-
-    current_item = "#session_"+slot_id; //slot_status_obj[0].session_id;
-    if(current_item != null){
-	$(current_item).addClass("selected_slot");
-        $(current_item).get(0).scrollIntoView(true);
-    }
-
-    if(ssk != null){
-	var slot_status_obj = agenda_globals.slot_status[ssk];
-	current_timeslot    = slot_status_obj[0].timeslot;
-	current_timeslot_id = slot_status_obj[0].timeslot_id;
-	ss = slot_status_obj[0];
-	session = ss.session;
-        last_session = session;
-        last_session.selectit();
-	// now set up the call back that might have to retrieve info.
-        fill_in_session_info(session, true, ss);
-    }
-    else {
-	ss = agenda_globals.meeting_objs[slot_id];
-        last_session = ss;
-        last_session.selectit();
-        fill_in_session_info(ss, true, ss.slot);
-    }
-
-    console.log("selecting new item:", last_session.title);
-}
-
-function set_pin_session_button(assignment) {
-    $("#pin_slot").unbind('click');
-    if(assignment == undefined) {
-        console.log("pin not set, assignment undefined");
-        return;
-    }
-    state = assignment.pinned;
-    //console.log("button set to: ",state);
-    $("#pin_slot").attr('disabled',false);
-    if(state) {
-        $("#pin_slot").html("unPin");
-        $("#agenda_pin_slot").addClass("button_down");
-        $("#agenda_pin_slot").removeClass("button_up");
-        $("#pin_slot").click(function(event) {
-            update_pin_session(assignment, false);
-        });
-    } else {
-        $("#pin_slot").html(" Pin ");
-        $("#agenda_pin_slot").addClass("button_up");
-        $("#agenda_pin_slot").removeClass("button_down");
-        $("#pin_slot").click(function(event) {
-            update_pin_session(assignment, true);
-        });
-    }
-}
-
-function update_pin_session(assignment, state) {
-    start_spin();
-    assignment.set_pinned(state, function(assignment) {
-        stop_spin();
-        session.repopulate_event(assignment.domid());
-        set_pin_session_button(assignment);
-    });
-}
-
-function enable_button(divid, buttonid, func) {
-    $(buttonid).unbind('click');
-    $(buttonid).click(func);
-    $(buttonid).attr('disabled',false);
-
-    $(divid).removeClass("button_disabled");
-    $(divid).addClass("button_enabled");
-}
-
-function disable_button(divid, buttonid) {
-    $(buttonid).unbind('click');
-    $(buttonid).attr('disabled',true);
-
-    $(divid).addClass("button_disabled");
-    $(divid).removeClass("button_enabled");
-}
-
-function highlight_session(session) {
-    var element = session.element()[0];
-    element.scrollIntoView(true);
-    session.element().parent().parent().parent().effect("pulsate", {color:"lightcoral"}, 10000);
-}
-
-function fill_in_session_info(session, success, extra) {
-    var prev_session = null;
-    var next_session = null;
-
-    if(session == null || session == "None" || !success){
-	empty_info_table();
-        return;
-    }
-    session.generate_info_table();
-
-    prev_session = session.prev_session;
-    next_session = session.next_session;
-
-    if(!read_only) {
-        enable_button("#agenda_double_slot", "#double_slot", extend_slot);
-
-        $("#agenda_pin_slot").removeClass("button_disabled");
-        $("#agenda_pin_slot").addClass("button_enabled");
-        set_pin_session_button(session.assignment);
-    } else {
-        $("#pin_slot").unbind('click');
-    }
-
-    enable_button("#agenda_show", "#show_session", show_all_session);
-
-    if(prev_session) {
-        enable_button("#agenda_prev_session", "#prev_session", function(event) {
-            select_session(prev_session);
-            highlight_session(prev_session);
-        });
-    } else {
-        disable_button("#agenda_prev_session", "#prev_session");
-    }
-    if(next_session) {
-        enable_button("#agenda_next_session", "#next_session", function(event) {
-            select_session(next_session);
-            highlight_session(next_session);
-        });
-    } else {
-        disable_button("#agenda_next_session", "#next_session");
-    }
-
-    $("#agenda_requested_features").html("");
-    // fill in list of resources into #agenda_requested_features
-    if(session.resources != undefined) {
-        $.each(session.resources, function(index) {
-            resource = this;
-
-            $("#agenda_requested_features").html(function(inbox, oldhtml) {
-                return "<div id=\"resource_"+resource.resource_id+"\"class=\"agenda_requested_feature\">"+
-                    "<img height=30 src=\""+
-                    resource.icon+"\" title=\""+
-                    resource.desc+"\" alt=\""+
-                    resource.name+
-                    "\" />"+
-                    "</div>"+
-                    oldhtml
-            });
-
-            console.log(session.title, "asks for resource", resource.desc);
-        });
-    }
-
-
-    // here is where we would set up the session request edit button.
-    // $(".agenda_sreq_button").html(session.session_req_link);
-
-    session.retrieve_constraints_by_session().success(function(newobj,status,jq) {
-        draw_constraints(session);
-    });
-}
-
-function group_name_or_empty(constraint) {
-    if(constraint == undefined) {
-	return "";
-    } else {
-	return constraint.conflict_view();
-    }
-}
-
-function draw_constraints(session) {
-
-    if("conflicts" in session) {
-        var display = agenda_globals.group_conflict_labels;
-        var group_icons = "";
-        var group_set = {};
-        $.each(session.conflicts, function(index) {
-            conflict = session.conflicts[index];
-            conflict.build_othername();
-            if(conflict.conflict_groupP()) {
-                if ( ! (conflict.othergroup_name in group_set) ) {
-                    group_set[conflict.othergroup_name] = {};
-                }
-                group_set[conflict.othergroup_name][conflict.direction]=display[conflict.conflict_type];
-                // This had been in build_group_conflict_view
-                conflict.populate_conflict_classes();
-                highlight_conflict(conflict);
-            }
-        });
-
-
-        $.each(group_set, function(index) {
-            group = group_set[index];
-            group_view = "<li class='conflict'>";
-            group_view += "<div class='conflictlevel'>"
-            if ('ours' in group_set[index]) {
-                group_view += group_set[index].ours+"->"
-            }
-            group_view += "</div>"
-            group_view += index;
-            group_view += "<div class='conflictlevel'>"
-            if ('theirs' in group_set[index]) {
-                group_view += "->"+group_set[index].theirs
-            }
-            group_view += "</div>"
-            group_view += "</li>";
-            group_icons += group_view;
-        });
-
-        if(group_icons == "") {
-            $("#conflict_group_list").html("none");
-        } else {
-            $("#conflict_group_list").html("<ul>"+group_icons+"</ul>");
-        }
-    } else {
-        $("#conflict_group_list").html("none");
-    }
-    if("bethere" in session.constraints) {
-        var people_icons = "";
-
-        $.each(session.constraints.bethere, function(index) {
-            conflict = session.constraints.bethere[index];
-            if(conflict.conflict_peopleP()) {
-                people_icons += "<li class='conflict'>"+conflict.conflict_view();
-            }
-        });
-        if(people_icons == "") {
-            $("#conflict_people_list").html("none");
-        } else {
-            $("#conflict_people_list").html("<ul>"+people_icons+"</ul>");
-        }
-    } else {
-        $("#conflict_people_list").html("none");
-    }
-
-}
-
-function highlight_conflict(constraint) {
-    if(constraint != undefined) {
-        var clicked = constraint.dom_id;
-        //console.log("91 fill", constraint.href, constraint.othergroup.href);
-	conflict_status[clicked] = true;
-	constraint.show_conflict_view();
-    }
-}
-
-var menu_bar_hidden = false;
-function hide_ietf_menu_bar(){
-    $('#ietf_menubar').toggle('slide',"",100);
-    if(menu_bar_hidden){
-	menu_bar_hidden = false;
-	$('.wrapper').css('width','auto');
-	$('.wrapper').css('margin-left','160px');
-	$('#close_ietf_menubar').html("<");
-
-    }
-    else{
-	menu_bar_hidden = true;
-	$('.wrapper').css('width','auto');
-	$('.wrapper').css('margin-left','0px');
-	$('#close_ietf_menubar').html(">");
-    }
-}
-
-
-
-/* create the droppable */
-function droppable(){
-    if(read_only) {
-	return;
-    }
-    $(function() {
-	/* the thing that is draggable */
-	$( ".meeting_event").draggable({
-	    appendTo: "body",
-	    helper: "clone",
-	    drag: drag_drag,
-	    start: drag_start,
-	    stop: drag_stop,
-	});
-
-	$( "#sortable-list").droppable({
-	    over : drop_over,
-	    activate: drop_activate,
-	    out : drop_out,
-	    drop : drop_drop,
-	    start: drop_start,
-	})
-
-	$("#meetings td.ui-droppable").droppable({
-	    over :drop_over,
-	    activate:drop_activate,
-	    out :drop_out,
-	    drop : drop_drop,
-	    create: drop_create,
-	    start: drop_start,
-
-	}); // end $(#meetings td).droppable
-    }); // end function()
-} // end droppable()
-
-
-function update_to_slot(session_id, to_slot_id, force){
-    //console.log("update_to_slot meeting_id:",session_id, to_slot_id);
-    if(to_slot_id == "sortable-list") {
-        /* must be bucket list */
-        return true;
-    }
-
-    var to_timeslot = agenda_globals.timeslot_bydomid[to_slot_id];
-    if(to_timeslot != undefined && (to_timeslot.empty == true || force)) {
-	// add a new assignment for this, save it.
-        var new_ss = make_ss({ "session_id" : session_id,
-                               "timeslot_id": to_timeslot.timeslot_id});
-        // make_ss also adds to slot_status.
-        var save_promise = new_ss.saveit();
-
-        if(to_slot_id != bucketlist_id) {
-	    to_timeslot.mark_occupied();
-        }
-
-	// update meeting_obj
-	agenda_globals.meeting_objs[session_id].placed(to_timeslot, true, new_ss);
-
-	return save_promise;
-    } else {
-        console.log("update_to_slot failed", to_timeslot, force, "empty", to_timeslot.empty);
-        return false;
-    }
-}
-
-function update_from_slot(session_id, from_slot_id)
-{
-    var from_timeslot      = agenda_globals.timeslot_bydomid[from_slot_id];
-
-    /* this is a list of schedule-timeslot-session-assignments */
-    var from_scheduledslots = agenda_globals.slot_status[from_slot_id];
-    var delete_promises = [];
-
-    // it will be null if it's coming from a bucketlist
-    if(from_slot_id != null && from_scheduledslots != undefined){
-        //console.log("1 from_slot_id", from_slot_id, from_scheduledslots);
-        var count = from_scheduledslots.length;
-        var found = [];
-	for(var k = 0; k<from_scheduledslots.length; k++) {
-            var from_ss = from_scheduledslots[k];
-	    if(from_ss.session_id == session_id){
-                found.push(from_ss)
-	    }
-	}
-        for (var k = 0; k<found.length; k++) {
-           // This will affect agenda_globals.slot_status[from_slot_id], hence from_scheduledslots
-           delete_promises.push(found[k].deleteit()); 
-        }
-        if (from_scheduledslots.length==0){
-            from_timeslot.mark_empty();
-        } 
-
-        /* remove undefined entries */
-        new_fromslots = [];
-        for(var k =0; k<from_scheduledslots.length; k++) {
-            if(from_scheduledslots[k] != undefined && from_scheduledslots[k].session_id != undefined) {
-                new_fromslots.push(from_scheduledslots[k]);
-            }
-        }
-
-        /* any removed sessions will have been done */
-        agenda_globals.slot_status[from_slot_id] = new_fromslots;
-        //console.log("2 from_slot_id", from_slot_id, new_fromslots);
-    }
-    else{
-        // this may be questionable.
-        // It deals with the fact that it's coming from the bucketlist.
-        delete_promises = [undefined];
-    }
-    return delete_promises;
-}
-
-
-
-function drop_drop(event, ui){
-
-    var session_id = ui.draggable.attr('session_id');   // the session was added as an attribute
-    // console.log("UI:", ui, session_id);
-    var session    = agenda_globals.meeting_objs[session_id];
-
-    var to_slot_id = $(this).attr('id'); // where we are dragging it.
-    var to_slot = agenda_globals.timeslot_bydomid[to_slot_id]
-
-    var from_slot_id = session.slot_status_key;
-    var from_slot = agenda_globals.slot_status[session.slot_status_key]; // remember this is an array...
-
-    move_thing({ "session": session,
-                 "to_slot_id": to_slot_id,
-                 "to_slot":    to_slot,
-                 "from_slot_id": from_slot_id,
-                 "from_slot":    from_slot,
-                 "event":        event,
-                 "ui":           ui,
-                 "dom_obj":      this});
-}
-
-function recalculate_conflicts_for_session(session, old_column_classes, new_column_classes)
-{
-    // go recalculate things
-    session.clear_all_conflicts();
-
-    find_and_populate_conflicts(session);
-    session.examine_people_conflicts(old_column_classes);
-
-    var sk;
-    for(sk in agenda_globals.meeting_objs) {
-        var s = agenda_globals.meeting_objs[sk];
-
-        for(ocn in old_column_classes) {
-            var old_column_class = old_column_classes[ocn];
-
-            if(old_column_class == undefined) continue;
-
-            for(ncn in new_column_classes) {
-                var new_column_class = new_column_classes[ncn];
-
-                if(new_column_class == undefined) continue;
-
-                //console.log("recalc? looking at ",s.title, old_column_class.column_tag, new_column_class.column_tag);
-                for(ccn in s.column_class_list) {
-                    var column_class = s.column_class_list[ccn];
-
-                    if(s != session && column_class != undefined &&
-                       (column_class.column_tag == new_column_class.column_tag||
-                        column_class.column_tag == old_column_class.column_tag)) {
-                        console.log("recalculating conflicts for:", s.title);
-                        s.clear_conflict();
-                        find_and_populate_conflicts(s);
-                        s.examine_people_conflicts();
-                    }
-                }
-            }
-        }
-    }
-    console.log("new conflict for ",session.title," is ", session.conflicted);
-    show_all_conflicts();
-}
-
-var __debug_parameters;
-var _LAST_MOVED;
-function move_slot(parameters) {
-    /* dom_obj: is a jquery selector of where the slot will be appeneded to
-       Naming is in regards to that most often function is called from
-       drop_drop where 'this' is the dom dest.
-    */
-    var update_to_slot_worked = false;
-    __debug_parameters = parameters;
-
-    if(agenda_globals.__debug_session_move) {
-        _LAST_MOVED = parameters.session;
-        if(parameters.from_slot != undefined) {
-            console.log("from_slot", parameters.from_slot.domid);
-        } else {
-            console.log("from_slot was unassigned");
-        }
-    }
-
-    /* if to slot was marked empty, then remove anything in it (like word "empty") */
-    if(parameters.to_slot.empty && !parameters.to_slot.unscheduled_box) {
-        $(parameters.dom_obj).html("");
-    }
-
-    var update_to_slot_worked = false;
-    if(parameters.same_timeslot == null){
-	parameters.same_timeslot = false;
-    }
-    var save_promise = undefined;
-    if(parameters.bucket_list) {
-	save_promise = update_to_slot(parameters.session.session_id,
-                                      parameters.to_slot_id, true);
-    }
-    else{
-	save_promise = update_to_slot(parameters.session.session_id,
-                                      parameters.to_slot_id, parameters.same_timeslot);
-    }
-
-    if(agenda_globals.__debug_session_move) {
-        console.log("update_to_slot returned promise", save_promise);
-    }
-
-    if(save_promise == false){
-	console.log("ERROR updating to_slot", save_promise,
-                    parameters.to_slot_id,
-                    agenda_globals.slot_status[parameters.to_slot_id]);
-	return undefined;
-    }
-
-    var delete_promises = update_from_slot(parameters.session.session_id, parameters.from_slot_id);
-    if(delete_promises.length > 0) {
-	remove_duplicate(parameters.from_slot_id, parameters.session.session_id);
-	// do something else?
-    }
-    else {
-	console.log("WARNING -- nothing to delete when updating from_slot", parameters.from_slot_id, agenda_globals.slot_status[parameters.from_slot_id]);
-	//return;
-    }
-    parameters.session.slot_status_key = parameters.to_slot_id;
-
-    var eTemplate = parameters.session.event_template()
-    $(parameters.dom_obj).append(eTemplate);
-
-    if(parameters.ui) {
-        parameters.ui.draggable.remove();
-    }
-
-    /* recalculate all the conflict classes given new slot */
-    parameters.session.update_column_classes([parameters.to_slot],
-                                             parameters.bucket_list);
-
-    /* set colours */
-    $(parameters.dom_obj).removeClass('highlight_free_slot');
-    if(parameters.to_slot.empty) {
-	$(parameters.dom_obj).removeClass('free_slot')
-    }
-    else{
-	$(parameters.dom_obj).addClass('free_slot')
-    }
-
-    if(parameters.from_slot != undefined && parameters.from_slot.empty){
-	$("#"+parameters.from_slot_id).removeClass('free_slot');
-    }
-    else{
-	$("#"+parameters.from_slot_id).addClass('free_slot');
-    }
-    $("#"+bucketlist_id).removeClass('free_slot');
-    /******************************************************/
-
-    droppable();
-    listeners();
-    sort_unassigned();
-
-    delete_promises.push(save_promise);
-    return $.when.apply($,delete_promises);
-}
-
-/* first thing that happens when we grab a meeting_event */
-function drop_activate(event, ui){
-    //$(event.draggable).css("background",dragging_color);
-    $(event.currentTarget).addClass('highlight_current_moving');
-}
-
-
-/* what happens when moving a meeting event over something that is 'droppable' */
-function drop_over(event, ui){
-    if(check_free(this)){
-	$(this).addClass('highlight_free_slot');
-    }
-    $(event.draggable).addClass('highlight_current_selected');
-    $(ui.draggable).addClass('highlight_current_selected');
-
-//    $(ui.draggable).css("background",dragging_color);
-//    $(event.draggable).css("background",dragging_color);
-}
-
-/* when we have actually dropped the meeting event */
-function drop_out(event, ui){
-    if(check_free(this)){
-	if($(this).attr('id') != bucketlist_id){
-	    $(this).addClass("free_slot");
-	}
-    }
-    $(this).removeClass('highlight_free_slot');
-    $(event.draggable).removeClass('highlight_current_selected');
-    $(ui.draggable).removeClass('highlight_current_selected');
-
-}
-
-function drag_stop(event,ui){
-    $(event.target).removeClass('highlight_current_selected');
-    $(event.target).removeClass('highlight_current_moving');
-}
-
-
-/* functions here are not used at the moment */
-function drop_create(event,ui){
-}
-
-function drop_start(event,ui){
-}
-
-function drag_drag(event, ui){
-}
-
-function drag_start(event, ui){
-    return;
-}
-
-/*
- * Local Variables:
- * c-basic-offset:4
- * End:
- */
diff --git a/ietf/static/ietf/js/agenda/agenda_objects.js b/ietf/static/ietf/js/agenda/agenda_objects.js
deleted file mode 100644
index 441f27b81..000000000
--- a/ietf/static/ietf/js/agenda/agenda_objects.js
+++ /dev/null
@@ -1,1935 +0,0 @@
-/*
-*
-*  FILE: agenda_objects.js
-* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE.
-*
-*   www.credil.org: Project Orlando 2013
-*   Author: Justin Hornosty ( justin@credil.org )
-*           Michael Richardson <mcr@sandelman.ca>
-*
-*  Description:
-*      Contains the objects relating to django's models.
-*      As much business logic as possible should be here.
-*      This file should be resuable by other than agenda_edit.js
-*
-*      Display logic should be contained in agenda_listeners.js
-*
-*   Functions:
-*      - check_delimiter(inp)
-*      - upperCaseWords(inp)
-*
-*/
-
-function AgendaGlobals() {
-    this.group_objs = {};
-    this.slot_status = {};
-    this.slot_objs   = {};
-    this.meeting_objs = {};
-    this.sessions_objs = {};
-    this.timeslot_bydomid = {};
-    this.timeslot_byid    = {};
-    this.assignment_promise = undefined;
-    this.timeslot_promise = undefined;
-    this.__debug_session_move = false;
-    /* Group_conflict_labels defines constraint names that are group conflicts and
-     * the symbols used to represent them. Shadow the old 1-2-3 labels to maintain
-     * behavior from before the new conflict types were introduced. This may need
-     * to be changed if the old editor is not phased out. */
-    this.group_conflict_labels = {
-        'conflict':'1' ,
-        'conflic2':'2' ,
-        'conflic3':'3',
-        'chair_conflict': '1',
-        'tech_overlap': '2',
-        'key_participant': '3'
-    };
-
-}
-
-function createLine(x1,y1, x2,y2){
-    var length = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
-  var angle  = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
-  var transform = 'rotate('+angle+'deg)';
-
-    var line = $('<div>')
-        .appendTo('#meetings')
-        .addClass('line')
-        .css({
-          'position': '',
-          'transform': transform
-        })
-        .width(length)
-        .offset({left: x1, top: y1});
-
-    return line;
-}
-
-
-function empty_callback(inp){
-//    console.log('inp:', inp);
-}
-
-function get_all_constraints(){
-    for(s in agenda_globals.meeting_objs){
-       show_non_conflicting_spots(s)
-    }
-
-}
-
-function show_all_conflicts(){
-    console.log("showing all conflicts");
-    for(sk in agenda_globals.meeting_objs) {
-        var s = agenda_globals.meeting_objs[sk];
-        s.display_conflict();
-        s.display_personconflict();
-    }
-}
-
-// not really used anymore -- just for debugging
-function hide_all_conflicts(){
-    for(sk in agenda_globals.meeting_objs) {
-        var s = agenda_globals.meeting_objs[sk];
-        s.hide_conflict();
-    }
-}
-
-function get_all_conflicts(){
-    var all_constraint_promises = [];
-    var one_constraint;
-    var sess1;
-    //console.log("get_all_conflicts()");
-    for(var s in agenda_globals.meeting_objs){
-        sess1 = agenda_globals.meeting_objs[s];
-        sess1.clear_conflict();
-        sess1.display_conflict();
-
-        one_constraint = sess1.retrieve_constraints_by_session();
-        all_constraint_promises.push(one_constraint);
-    }
-
-    /* now make a promise that ends when all the conflicts are loaded */
-    var all_constraints = $.when.apply($,all_constraint_promises);
-
-    all_constraints.done(function() {
-        for(var s in agenda_globals.meeting_objs) {
-            var sess2 = agenda_globals.meeting_objs[s];
-            sess2.examine_people_conflicts();
-        }
-    });
-    return all_constraints;
-}
-
-var __debug_conflict_calculate = false;
-var __verbose_conflict_calculate = false;
-
-function calculate_real_conflict(conflict, vertical_location, room_tag, session_obj) {
-    if(__debug_conflict_calculate) {
-        console.log(" conflict check:", conflict.othergroup.acronym, "me:", vertical_location, room_tag);
-    }
-
-    if(session_obj.group.href == conflict.othergroup.href) {
-        console.log("  session: ",session_obj.session_id, "lists conflict with self");
-        return;
-    }
-
-    var osessions = conflict.othergroup.all_sessions;
-    if(__debug_conflict_calculate) {
-        console.log("  ogroup: ", conflict.othergroup.href, "me: ", session_obj.group.href);
-    }
-    if(conflict.othergroup === session_obj.group) {
-        osessions = conflict.thisgroup.all_sessions;
-    }
-    if(osessions != null) {
-        $.each(osessions, function(index) {
-            osession = osessions[index];
-            for(ccn in osession.column_class_list) {
-                var value = osession.column_class_list[ccn];
-                if(value != undefined) {
-                    if(__debug_conflict_calculate) {
-                        console.log("    vs: ",index, "session_id:",osession.session_id," at: ",value.column_tag, value.room_tag);
-                    }
-                    if(value.column_tag == vertical_location &&
-                       value.room_tag   != room_tag) {
-                        if(__verbose_conflict_calculate || __debug_conflict_calculate) {
-                            console.log("real conflict:",session_obj.title," with: ",conflict.othergroup.acronym, " #session_",session_obj.session_id, value.room_tag, room_tag, value.column_tag, vertical_location);
-                        }
-                        // there is a conflict!
-                        __DEBUG_SHOW_CONSTRAINT = $("#"+value[0]).children()[0];
-                        session_obj.add_conflict(conflict);
-                    }
-                }
-            }
-        });
-    }
-}
-
-var __DEBUG_SHOW_CONSTRAINT = null;
-// can become a method now.
-function find_and_populate_conflicts(session_obj) {
-    if(__debug_conflict_calculate) {
-        console.log("populating conflict:", session_obj.title, session_obj.column_class_list);
-    }
-
-    var room_tag = null;
-    session_obj.reset_conflicts();
-
-    for(let ccn in session_obj.column_class_list) {
-        if (session_obj.column_class_list.hasOwnProperty(ccn)) {
-            var vertical_location = session_obj.column_class_list[ccn].column_tag;
-            var room_tag          = session_obj.column_class_list[ccn].room_tag;
-            $.each(Object.keys(agenda_globals.group_conflict_labels), (i, conflict_name) => {
-                if (session_obj.constraints[conflict_name]) {
-                    $.each(session_obj.constraints[conflict_name], (j, conflict) => {
-                        calculate_real_conflict(conflict, vertical_location, room_tag, session_obj);
-                    });
-                }
-                if(session_obj.theirconstraints.conflict){
-                    $.each(session_obj.theirconstraints.conflict, (i, conflict) => {
-                        calculate_real_conflict(conflict, vertical_location, room_tag, session_obj);
-                    });
-                }
-              }
-            );
-
-            /* bethere constraints are processed in another loop */
-        }
-    }
-}
-
-function show_non_conflicting_spots(ss_id){
-    var conflict_spots = []
-    $.each(conflict_classes, function(key){
-       conflict_spots.push(conflict_classes[key].session.slot_status_key);
-    });
-    var empty_slots = find_empty_slots();
-    conflict_spots.forEach(function(val){
-       empty_slots.forEach(function(s){
-           if(val == s.key){
-           }
-       });
-    });
-}
-
-function find_empty_slots(){
-    var empty_slots = [];
-    $.each(slot_status, function(key){
-       for(var i =0; i<slot_status[key].length; i++){
-           if(slot_status[key][i].empty == "True" || slot_status[key][i].empty == true){
-              var pos = { "index" :i, key:key } ;
-              empty_slots.push(pos);
-           }
-       }
-    });
-    return empty_slots;
-}
-
-
-// ++++++++++++++++++
-// Slot Object
-function slot(){
-}
-
-/*
-   check_delimiter(inp), where inp is a string.
-       returns char.
-
-   checks for what we should split a string by.
-   mainly we are checking for a '/' or '-' character
-
-   Maybe there is a js function for this. doing 'a' in "abcd" does not work.
- */
-function check_delimiter(inp){
-    for(var i =0; i<inp.length; i++){
-       if(inp[i] == '/'){
-           return '/';
-       }
-       else if(inp[i] == '-'){
-           return '-';
-       }
-    }
-    return ' ';
-
-}
-
-/*
-   upperCaseWords(inp), where inp is a string.
-       returns string
-
-   turns the first letter of each word in a string to uppercase
-   a word is something considered be something defined by the function
-   check_delimiter(). ( '/' , '-' , ' ' )
-*/
-function upperCaseWords(inp){
-    var newStr = "";
-    var split = inp.split(check_delimiter(inp));
-
-       for(i=0; i<split.length; i++){
-              newStr = newStr+split[i][0].toUpperCase();
-              newStr = newStr+split[i].slice(1,split[i].length);
-
-              if(i+1 < split.length){ // so we don't get a extra space
-                     newStr = newStr+" ";
-              }
-    }
-
-       return newStr;
-
-}
-
-var daysofweek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
-
-// ++++++++++++++++++
-// ColumnClass is an object that knows about columns, but also about
-// columns + room (so it can identify a single slot, or a column of them)
-function ColumnClass(room,date,time) {
-    this.room = room;
-    this.date = date;
-    this.time = time;
-    this.room_tag   = this.room+"_"+this.date+"_"+this.time;
-    this.th_time    = this.date+"-"+this.time;
-    this.column_tag = ".agenda-column-"+this.th_time;
-    this.th_tag     = ".day_" + this.th_time;
-};
-
-
-// ++++++++++++++++++
-// TimeSlot Object
-//
-//   { "timeslot_id":"{{timeslot.id}}",
-//     "room"       :"{{timeslot.location|slugify}}",
-//     "time"       :"{{timeslot.time|date:'Hi' }}",
-//     "date"       :"{{timeslot.time|date:'Y-m-d'}}",
-//     "domid"      :"{{timeslot.js_identifier}}"}
-function TimeSlot(){
-    this.timeslot_id  = undefined;
-    this.room         = undefined;
-    this.time         = undefined;
-    this.date         = undefined;
-    this.domid        = undefined;
-    this.empty        = true;
-    this.assignments = [];
-    this.following_timeslot_id = undefined;
-    this.unscheduled_box = false;
-}
-
-TimeSlot.prototype.initialize = function(json) {
-    for(var key in json) {
-       this[key]=json[key];
-    }
-    //console.log("timeslot processing: ", this.timeslot_id,
-    //            this.room, this.date, this.time);
-
-    this.column_class=new ColumnClass(this.room, this.date, this.time);
-
-    this.day   = new Date(this.date);
-    this.starttime = parseInt(this.time,10);
-    var t = this.day.getUTCDay();
-    if(this.room == "Unassigned"){
-       this.short_string = "Unassigned";
-    }
-    else{
-       this.short_string = daysofweek[t] + ", "+ this.time + ", " + this.room;
-    }
-
-    agenda_globals.timeslot_bydomid[this.domid] = this;
-    agenda_globals.timeslot_byid[this.timeslot_id] = this;
-};
-
-TimeSlot.prototype.title = function() {
-    return this.room + " "+this.date+" "+this.time;
-};
-TimeSlot.prototype.slot_title = function() {
-    return "id#"+this.timeslot_id+" dom:"+this.domid;
-};
-TimeSlot.prototype.mark_empty = function() {
-    if(agenda_globals.__debug_session_move) {
-        console.log("marking slot empty", this.domid);
-        $("#"+this.domid).html("empty");
-    }
-    this.empty = true;
-};
-TimeSlot.prototype.mark_occupied = function() {
-    if(agenda_globals.__debug_session_move) {
-        console.log("marking slot occupied", this.domid);
-
-        // if it was empty before, then clear it (might have word "empty" in it)
-        if(this.empty == true) {
-            $("#"+this.domid).html("");
-        }
-    }
-    this.empty = false;
-};
-TimeSlot.prototype.can_extend_right = function() {
-    if(this.following_timeslot == undefined) {
-        if(this.following_timeslot_id != undefined) {
-            this.following_timeslot = agenda_globals.timeslot_byid[this.following_timeslot_id];
-        }
-    }
-    if(this.following_timeslot == undefined) {
-        console.log("can_extend_right:",this.timeslot_id," no slot to check");
-        return false;
-    } else {
-        console.log("can_extend_right:",
-                    this.slot_title()," for slot: ",
-                    this.following_timeslot.slot_title(),
-                    "is ",this.following_timeslot.empty);
-        return this.following_timeslot.empty;
-    }
-};
-
-function make_timeslot(json) {
-    var ts = new TimeSlot();
-    ts.initialize(json);
-    return ts;
-}
-
-/* feed this an array of timeslots */
-function make_timeslots(json, status, jqXHR) {
-    $.each(json, function(index) {
-        var thing = json[index];
-        make_timeslot(thing);
-    });
-}
-
-function load_timeslots(href) {
-    if(agenda_globals.timeslot_promise == undefined) {
-        agenda_globals.timeslot_promise = $.Deferred();
-        var ts_promise = agenda_globals.timeslot_promise;
-
-        var ts = $.ajax(href);
-        ts.success(function(newobj, status, jqXHR) {
-            make_timeslots(newobj);
-            ts_promise.resolve(newobj);
-            console.log("finished timeslot promise");
-        });
-    }
-    return agenda_globals.timeslot_promise;
-}
-
-
-// ++++++++++++++++++
-// ScheduledSlot Object
-// SchedTimeSessAssignment is the DJANGO name for this object
-// It represents a TimeSlot that can be assigned in this schedule.
-//   { "assignment_id": "{{s.id}}",
-//     "href:        "{{s.href}}",
-//     "timeslot_id":"{{s.timeslot.id}}",
-//     "session_id" :"{{s.session.id}}",
-//     "extendedfrom_id"    :refers to another schedtimesessassignment by ss.id
-function ScheduledSlot(){
-    this.extendedfrom = undefined;
-    this.extendedto   = undefined;
-    this.extendedfrom_id = false;
-    this.pinned       = false;
-}
-
-ScheduledSlot.prototype.room = function() {
-    return this.timeslot.room;
-};
-
-ScheduledSlot.prototype.time = function() {
-    return this.timeslot.time;
-};
-
-ScheduledSlot.prototype.date = function() {
-    return this.timeslot.date;
-};
-
-ScheduledSlot.prototype.domid = function() {
-    return this.timeslot.domid;
-};
-
-ScheduledSlot.prototype.column_class = function() {
-    return this.timeslot.column_class;
-};
-
-ScheduledSlot.prototype.short_string = function() {
-    return this.timeslot.short_string;
-};
-
-ScheduledSlot.prototype.saveit = function() {
-    var myss = this;
-    stuffjson = new Object();
-    stuffjson.session_id  = this.session_id;
-    stuffjson.timeslot_id = this.timeslot_id;
-    if(this.extendedfrom_id != undefined && this.extendedfrom_id != false) {
-        stuffjson.extendedfrom_id = this.extendedfrom_id;
-    }
-
-    var stuff = JSON.stringify(stuffjson, null, '\t');
-
-    var saveit = $.ajax(assignments_post_href,{
-        "content-type": "application/json",
-        "type": "POST",
-        "data": stuff,
-    });
-    // should do something on success and failure.
-    saveit.done(function(result, status, jqXHR) {
-        myss.initialize(result, "saveit");
-        var session = myss.session;
-        if(session != undefined) {
-            session.placed(myss.timeslot);
-        }
-    });
-    saveit.fail(function(jqXHR, textStatus) {
-        var xhr = JSON.parse(jqXHR.responseText);
-        alert("ERROR: " + xhr.error + "\nThe schedule will now reload.");
-        location.reload(true);
-    });
-
-    // return the promise, in case someone (tests!) needs to know when we are done.
-    return saveit;
-};
-
-ScheduledSlot.prototype.set_pinned = function(state, completefunc) {
-    var ss = this;
-    var pinned_struct = { "pinned" : state };
-    var pinned_update = $.ajax(this.href, {
-        "content-type": "application/json",
-        "type": "PUT",
-        "data": pinned_struct,
-    });
-
-    pinned_update.success(function(result, status, jqXHR) {
-        if(result.message != "valid") {
-            alert("Update of pinned failed");
-            return;
-        }
-        ss.pinned = state;
-        completefunc(this);
-    });
-};
-
-
-
-function remove_from_slot_status(domid, ss_id) {
-    var found_at;
-    if(agenda_globals.__debug_session_move) {
-        console.log("remove", domid, agenda_globals.slot_status[domid]);
-    }
-    if(agenda_globals.slot_status[domid] != undefined) {
-        $.each(agenda_globals.slot_status[domid], function(index, value) {
-            if(agenda_globals.__debug_session_move) {
-                console.log("  checking", index, value, ss_id);
-            }
-            if(value.assignment_id == ss_id) {
-                found_at = index;
-                return;
-            }
-        });
-        if(found_at != undefined) {
-            agenda_globals.slot_status[domid].splice(found_at, 1);
-            console.log("removed", found_at, agenda_globals.slot_status[domid]);
-        }
-    } else {
-        //console.log("  already empty");
-    }
-};
-
-ScheduledSlot.prototype.deleteit = function() {
-    var deleteit = $.ajax(this.href, {
-        "content-type": "application/json",
-        "type": "DELETE",
-    });
-    // now nuke self!
-    var me = this;
-    delete agenda_globals.slot_objs[this.assignment_id];
-    remove_from_slot_status(this.domid(), this.assignment_id);
-    return deleteit;
-};
-
-function update_if_not_undefined(old, newval) {
-    if(newval != undefined) {
-        return newval;
-    } else {
-        return old;
-    }
-}
-
-ScheduledSlot.prototype.make_unassigned = function() {
-    this.assignment_id	     = 0;
-    this.empty               = true;
-    this.session_id          = null;
-    this.room                = "unassigned";
-    this.time                = null;
-    this.date                = null;
-    this.timeslot            = new TimeSlot();
-    this.timeslot.initialize({"domid":"sortable-list"});
-    this.timeslot.unscheduled_box = true;
-    this.timeslot.short_string = "Unscheduled";
-
-
-    agenda_globals.slot_status[this.domid()]=[];
-    agenda_globals.slot_status[this.domid()].push(this);
-    agenda_globals.slot_objs[this.assignment_id] = this;
-};
-
-ScheduledSlot.prototype.real_initialize = function(json, extra) {
-    /* do not copy everything over */
-    this.pinned              = update_if_not_undefined(this.pinned, json.pinned);
-    this.assignment_id	     = update_if_not_undefined(this.assignment_id, json.assignment_id);
-    this.session_id          = update_if_not_undefined(this.session_id, json.session_id);
-    this.timeslot_id         = update_if_not_undefined(this.timeslot_id, json.timeslot_id);
-    this.href                = update_if_not_undefined(this.href, json.href);
-    this.extendedfrom_id     = update_if_not_undefined(this.extendedfrom_id, json.extendedfrom_id);
-
-    //console.log("timeslot_id", this.timeslot_id);
-    this.timeslot            = agenda_globals.timeslot_byid[this.timeslot_id];
-    if(this.timeslot != undefined && this.session_id != undefined) {
-        this.timeslot.mark_occupied();
-    }
-    if(this.session_id != undefined) {
-        this.session = agenda_globals.meeting_objs[this.session_id];
-    }
-
-    // translate Python booleans to JS.
-    if(this.pinned == "True") {
-        this.pinned = true;
-    } else {
-        this.pinned = false;
-    }
-
-    // timeslot should never be null, but if there is old/flaky
-    // data it could happen, so guard against it.
-    if(this.timeslot != undefined) {
-        // do not include in data structures if session_id is nil
-        // the key so two sessions in the same timeslot
-        if(agenda_globals.slot_status[this.domid()] == null) {
-            agenda_globals.slot_status[this.domid()]=[];
-        }
-        if(this.session_id != undefined) {
-            // remove any old duplicate that might exist.
-            remove_from_slot_status(this.domid(), this.assignment_id);
-            if(agenda_globals.__debug_session_move) {
-                console.log(extra, "adding to slot_status", this.domid());
-            }
-
-            agenda_globals.slot_status[this.domid()].push(this);
-            //console.log("filling slot_objs", this.assignment_id);
-        }
-    }
-
-    agenda_globals.slot_objs[this.assignment_id] = this;
-};
-ScheduledSlot.prototype.initialize = ScheduledSlot.prototype.real_initialize;
-
-function load_assignments(ts_promise, session_promise, href) {
-    if(agenda_globals.assignment_promise == undefined) {
-        agenda_globals.assignment_promise = $.Deferred();
-
-        var ss = $.ajax(href);
-        var ss_loaded = $.when(ss,ts_promise,session_promise);
-
-        ss_loaded.done(function(result, status, jqXHR) {
-            console.log("finished ss promise");
-
-            newobj = result[0]
-            $.each(newobj, function(index) {
-                one = newobj[index];
-                //console.log("ss has:", one);
-                make_ss(one);
-            });
-            agenda_globals.assignment_promise.resolve(newobj);
-        });
-    }
-    return agenda_globals.assignment_promise;
-}
-
-ScheduledSlot.prototype.connect_to_timeslot_session = function() {
-    if(this.timeslot == undefined) {
-        if(this.timeslot_id != undefined) {
-            this.timeslot = agenda_globals.timeslot_byid[this.timeslot_id];
-        } else {
-            /* must be the unassigned one?! */
-            this.timeslot = new TimeSlot();
-            this.timeslot.domid = "sortable-list";
-        }
-    }
-    /* session could be hooked up, but it is now always session() */
-};
-
-ScheduledSlot.prototype.session = function() {
-    if(this.session_id != undefined) {
-       return agenda_globals.meeting_objs[this.session_id];
-    } else {
-        console.log("ss id:", this.assignment_id, "timeslot:", this.timeslot_id, this.timeslot.title(), "has null session");
-        return undefined;
-    }
-};
-ScheduledSlot.prototype.slot_title = function() {
-    return "id#"+this.assignment_id+" dom:"+this.domid();
-};
-
-function make_ss(json) {
-    var ss = new ScheduledSlot();
-    ss.initialize(json, "make_ss");
-    return ss;
-}
-
-
-// ++++++++++++++++++
-// Session Objects
-//
-// initialized by loading a json from /meeting/XX/sessions.json, return JSON that looks like:
-//
-//               {"title" : "{{ s.short_name }}",
-//                "description":"{{ s.group.name }}",
-//                "special_request": "{{ s.special_request_token }}",
-//                "session_id":"{{s.id}}",
-//                "owner": "{{s.owner.owner}}",
-//                "area":"{{s.group.parent.acronym|upper}}",
-//                "duration":"{{s.requested_duration.seconds|durationFormat}}"});
-//
-
-
-function Session() {
-    this.constraints = {};
-    this.theirconstraints = {};
-    this.constraint_load_andthen_list = [];
-    this.constraints_loaded = false;
-    this.last_timeslot_id = null;
-    this.slot_status_key = null;
-    this.href       = false;
-    this.group      = undefined;
-    this.column_class_list = [];
-    this.loaded = undefined;
-    this.area = "noarea";
-    this.special_request = "";
-    this.conflicted = false;
-    this.conflicted_direction = {};
-    this.theirconflicted = false;
-    this.double_wide = false;
-    this.attendees   = undefined;
-}
-
-function session_obj(json) {
-    session = new Session();
-
-    for(var key in json) {
-        //console.log("copying", key, "value: ", json[key]);
-        if(json[key] != undefined && json[key] != "") {
-            session[key]=json[key];
-        }
-    }
-    // dict will not pass .length > 0 above.
-    session.group = json.group;
-
-    if(session.requested_duration == undefined) {
-        session.requested_duration = session.duration;
-    }
-
-    // make it a number.
-    session.attendees = parseInt(session.attendees);
-
-    session.ogroup = session.group;
-    if(session.group != undefined) {
-        /* it has an inline group object, intern it, and redirect to interned object */
-        //console.log(session.title, "using embedded group: ",
-        //            session.group.acronym, session.group.href, session.group);
-        session.group = load_group_from_json(session.group);
-        session.group_href = session.group.href;
-        //console.log(session.title, "2 using embedded group: ",
-        //            session.group.acronym, session.group.href, session.group);
-    } else if(session.group_href != undefined) {
-        console.log("session ",session.session_id,
-                    "has no embedded group, load by href", session.group_href);
-        session.group = find_group_by_href(session.group_href, "session_load");
-    } else {
-        // bogus
-        session.group_href = site_base_url+'/group/'+session.title+".json";
-    }
-
-    // keep a list of sessions by name
-    // this is mostly used for debug purposes only.
-    if(agenda_globals.sessions_objs[session.title] == undefined) {
-        agenda_globals.sessions_objs[session.title] = [];
-    }
-    agenda_globals.sessions_objs[session.title].push(session);   // an array since there can be more than one session/wg
-
-    agenda_globals.meeting_objs[session.session_id] = session;
-
-    return session;
-}
-
-/* feed this an array of sessions */
-function make_sessions(json, status, jqXHR) {
-    $.each(json, function(index) {
-        var thing = json[index];
-        session_obj(thing);
-    });
-}
-
-function load_sessions(href) {
-    if(agenda_globals.session_promise == undefined) {
-        agenda_globals.session_promise = $.Deferred();
-
-        var ss = $.ajax(href);
-        ss.success(function(newobj, status, jqXHR) {
-            console.log("finished session promise");
-            make_sessions(newobj);
-            agenda_globals.session_promise.resolve(newobj);
-        });
-    }
-    return agenda_globals.session_promise;
-}
-
-function count_sessions() {
-    $.each(agenda_globals.sessions_objs, function(title) {
-        //console.log("title", title, this);
-        var lastone  = null;
-        var sessions = agenda_globals.sessions_objs[title];
-        var num_sessions = sessions.length;
-        $.each(sessions, function(index) {
-            //console.log("session", index, this);
-            this.number = index;          // a number
-            this.maxNum = num_sessions;
-
-            this.prev_session = lastone;
-            this.next_session = null;
-            if(index < num_sessions) {
-                this.next_session = sessions[index+1];
-            }
-            lastone = this;
-        });
-    });
-}
-
-
-// augument to jQuery.getJSON( url, [data], [callback] )
-Session.prototype.load_session_obj = function(andthen, arg) {
-    session = this;
-    if(this.loaded == undefined) {
-        start_spin();
-        this.loaded = $.ajax(this.href);
-    }
-
-    this.loaded.success(function(newobj, status, jqXHR) {
-        last_json_reply = newobj;
-        $.extend(session, newobj);
-
-        if(andthen != undefined) {
-            andthen(session, true, arg);
-        }
-        stop_spin();
-    });
-    this.loaded.error(function(jqXHR, textStatus, errorThrown ) {
-        console.log("exception: ",textStatus,errorThrown);
-        if(andthen != undefined) {
-            andthen(session, false, arg);
-        }
-        stop_spin();
-    });
-};
-
-Session.prototype.find_responsible_ad = function() {
-    var session = this;
-    //console.log("session",this.title, this.session_id,"looking for ad",this.group.ad_href);
-    if(this.group && this.group.ad_href) {
-        find_person_by_href(session.group.ad_href).done(function(ad) {
-            //console.log("session",session.session_id,"found ", ad);
-            session.responsible_ad = ad;
-        });
-    }
-};
-
-Session.prototype.element = function() {
-    return $("#session_"+this.session_id);
-};
-
-Session.prototype.personconflict_element = function() {
-    return this.element().parent().find(".personconflict");
-};
-
-Session.prototype.selectit = function() {
-    clear_all_selections();
-    // mark self as selected
-    if(this.group != undefined) {
-        $("." + this.group.acronym).addClass("same_group");
-    }
-    this.element().removeClass("save_group");
-    this.element().addClass("selected_group");
-};
-Session.prototype.unselectit = function() {
-    clear_all_selections();
-};
-
-Session.prototype.on_bucket_list = function() {
-    this.is_placed = false;
-    this.column_class_list = [];
-    this.element().parent("div").addClass("meeting_box_bucket_list");
-};
-Session.prototype.placed = function(where, forceslot, assignment) {
-    this.is_placed = true;
-
-    // forceslot is set on a move, but unset on initial placement,
-    // as placed might be called more than once for a double slot session.
-    if(forceslot || this.slot==undefined) {
-        this.slot      = where;
-        this.assignment = assignment;
-
-        /* we can not mark old slot as empty, because it
-           might have multiple sessions in it.
-           we can do the opposite: mark it was not empty when we fill it.
-        */
-
-        if(where != undefined) {
-            where.empty = false;
-        }
-    }
-    if(where != undefined) {
-        this.add_column_class(where.column_class);
-    }
-    //console.log("session:",session.title, "column_class", ssid.column_class());
-    this.element().parent("div").removeClass("meeting_box_bucket_list");
-    this.pinned = where.pinned;
-};
-
-Session.prototype.populate_event = function(js_room_id) {
-    var eTemplate =     this.event_template()
-    insert_cell(js_room_id, eTemplate, false);
-};
-Session.prototype.repopulate_event = function(js_room_id) {
-    var eTemplate =     this.event_template()
-    insert_cell(js_room_id, eTemplate, true);
-    this.display_conflict();
-};
-
-Session.prototype.visible_title = function() {
-    return this.special_request + this.title;
-};
-
-var _conflict_debug = false;
-Session.prototype.mark_conflict = function(value) {
-    this.conflicted = value;
-};
-Session.prototype.add_conflict = function(conflict) {
-    this.conflicted = true;
-    if (this.conflicted_direction==undefined) {
-        this.conflicted_direction={};
-    }
-    this.conflicted_direction[conflict.direction] = true;
-    if (this.highest_conflict==undefined) {
-        this.highest_conflict={};
-    }
-    if(this.highest_conflict[conflict.direction]==undefined) {
-        this.highest_conflict[conflict.direction] = conflict;
-    } else {
-        var oldhighest = this.highest_conflict[conflict.direction];
-        this.highest_conflict[conflict.direction] = this.highest_conflict[conflict.direction].conflict_compare(conflict);
-        if(_conflict_debug) {
-            console.log("add conflict for", this.title,
-                        oldhighest.conflict_type, ">?", conflict.conflict_type,
-                        "=", this.highest_conflict[conflict.direction].conflict_type);
-        }
-    }
-    if (this.conflict_level==undefined){
-        this.conflict_level={};
-    }
-    this.conflict_level[conflict.direction] = this.highest_conflict[conflict.direction].conflict_type;
-};
-
-Session.prototype.clear_conflict = function() {
-    this.conflicted = false;
-};
-
-Session.prototype.clear_all_conflicts = function(old_column_classes) {
-    var session_obj = this;
-    this.clear_conflict();
-
-    if(old_column_classes != undefined) {
-        $.each(session_obj.constraints.bethere, function(i) {
-            var conflict = session_obj.constraints.bethere[i];
-            var person = conflict.person;
-
-            person.clear_session(session_obj, old_column_classes);
-        });
-    }
-};
-
-Session.prototype.show_conflict = function() {
-    if(_conflict_debug) {
-        console.log("showing conflict for", this.title, this.conflict_level['ours'],this.conflict_level['theirs']);
-    }
-    var display = agenda_globals.group_conflict_labels;
-    if (this.conflicted) {
-        if ('ours' in this.conflict_level) {
-            this.element().find('.ourconflicts').text('->'+display[this.conflict_level.ours]);
-        }
-        if ('theirs' in this.conflict_level) {
-            this.element().find('.theirconflicts').text(display[this.conflict_level.theirs]+'->');
-        }
-    }
-};
-
-Session.prototype.hide_conflict = function() {
-    if(_conflict_debug) {
-        console.log("removing conflict for", this.title);
-    }
-    this.element().find('.ourconflicts').text('');
-    this.element().find('.theirconflicts').text('');
-};
-
-Session.prototype.display_conflict = function() {
-    if(this.conflicted || this.theirconflicted) {
-        this.show_conflict();
-    } else {
-        this.hide_conflict();
-    }
-};
-
-Session.prototype.reset_conflicts = function() {
-    this.conflict_level = undefined;
-    this.highest_conflict = undefined;
-    this.conflicted = false;
-    this.conflicted_direction = undefined;
-    this.theirconflict_level = undefined;
-    this.highest_theirconflict = undefined;
-    this.theirconflicted = false;
-};
-
-Session.prototype.show_personconflict = function() {
-    if(_conflict_debug) {
-        console.log("showing person conflict for", this.title, this.conflict_level.ours);
-    }
-    this.personconflict_element().removeClass("hidepersonconflict");
-    this.personconflict_element().addClass("showpersonconflict");
-};
-Session.prototype.hide_personconflict = function() {
-    if(_conflict_debug) {
-        console.log("removing person conflict for", this.title);
-    }
-    this.personconflict_element().addClass("hidepersonconflict");
-    this.personconflict_element().removeClass("showpersonconflict");
-};
-Session.prototype.display_personconflict = function() {
-    if(this.person_conflicted) {
-        this.show_personconflict();
-    } else {
-        this.hide_personconflict();
-    }
-};
-Session.prototype.add_personconflict = function(conflict) {
-    this.person_conflicted = true;
-};
-
-Session.prototype.examine_people_conflicts = function() {
-    /*
-     * the scan for people conflicts has to be done after the fill_in_constraints
-     * because we don't know which sessions the people will need to attend until
-     * all the constraints have been examined.
-     */
-    var session_obj = this;
-
-    // reset people conflicts.
-    session_obj.person_conflicted = false;
-
-    for(ccn in session_obj.column_class_list) {
-        var vertical_location = session_obj.column_class_list[ccn].column_tag;
-        var room_tag          = session_obj.column_class_list[ccn].room_tag;
-
-        if(session_obj.constraints.bethere != null) {
-            if(_person_bethere_debug) {
-                console.log("examining bethere constraints for", session_obj.title);
-            }
-            $.each(session_obj.constraints.bethere, function(i) {
-                var conflict = session_obj.constraints.bethere[i];
-                find_person_by_href(conflict.person_href).done(function(person) {
-                    conflict.person = person;
-                    if(_person_bethere_debug) {
-                        console.log("examining", person.ascii," bethere constraints for", session_obj.title);
-                    }
-                    if(person.conflicted_time(vertical_location)) {
-                        session_obj.add_personconflict(conflict);
-                    }
-                });
-            });
-        }
-    }
-}
-
-
-Session.prototype.area_scheme = function() {
-    return this.area.toUpperCase() + "-scheme";
-};
-
-Session.prototype.is_bof = function() {
-    return this.bof == "True";
-}
-Session.prototype.wg_scheme = function() {
-    if(this.is_bof()) {
-        return "bof_style";
-    } else {
-        return "wg_style";
-    }
-};
-
-Session.prototype.add_column_class = function(column_class) {
-    if(__column_class_debug) {
-        console.log("adding:",column_class, "to ", this.title);
-    }
-    this.column_class_list.push(column_class);
-};
-
-var _LAST_MOVED_OLD;
-var _LAST_MOVED_NEW;
-// timeslot_list is a list of slots where the session has been located.
-// bucket_list is a boolean.
-Session.prototype.update_column_classes = function(timeslot_list, bucket_list) {
-
-    // COLUMN CLASSES MUST BE A LIST because of multiple slot use
-    console.log("updating column_classes for ", this.title);
-
-    var old_column_classes = this.column_class_list;
-    if(old_column_classes.length == 0) {
-        console.log("old column class was undefined for session:", session.title);
-        old_column_classes = [new ColumnClass()];
-    }
-
-    // zero out list.
-    this.column_class_list = [];
-    var new_column_tag = "none";
-    if(bucket_list) {
-        this.on_bucket_list();
-
-    } else {
-        for(tsn in timeslot_list) {
-            var ts = timeslot_list[tsn];
-            console.log("timeslot_list", tsn, ts);
-            this.add_column_class(ts.column_class);
-        }
-        new_column_tag = this.column_class_list[0].column_tag;
-    }
-
-    var old_column_class_name = "none";
-    if(old_column_classes != undefined &&
-       old_column_classes[0] != undefined) {
-        old_colum_class_name = old_column_classes[0].column_tag;
-    }
-
-    console.log("setting column_class for ",this.title," to ",
-                new_column_tag, "was: ", old_column_class_name);
-
-    console.log("unset conflict for ",this.title," is ", this.conflicted);
-
-    _LAST_MOVED_OLD = old_column_classes;
-    _LAST_MOVED_NEW = this.column_class_list;
-
-    this.group.del_column_classes(old_column_classes);
-    this.group.add_column_classes(this.column_class_list);
-    recalculate_conflicts_for_session(this, old_column_classes, this.column_class_list);
-};
-
-
-// utility/debug function, draws all events.
-function update_all_templates() {
-    for(key in agenda_globals.meeting_objs) {
-        session = agenda_globals.meeting_objs[key];
-        var slot = session.slot_status_key;
-	if(slot != null) {
-            session.repopulate_event(slot);
-        }
-    }
-}
-
-Session.prototype.event_template = function() {
-    // the extra div is present so that the table can have a border which does not
-    // affect the total height of the box.  The border otherwise screws up the height,
-    // causing things to the right to avoid this box.
-    var area_mark = "";
-    if(this.responsible_ad != undefined) {
-        area_mark = this.responsible_ad.area_mark;
-        if(area_mark == undefined) {
-            // This doesn't seem to do the right thing
-            // area_mark = "ad:" + this.responsible_ad.href;
-            area_mark = "";
-        }
-    }
-
-    var bucket_list_style = "meeting_box_bucket_list"
-    if(this.is_placed) {
-        bucket_list_style = "";
-        area_mark = "";        /* no mark for unplaced items: it goes to the wrong place */
-    }
-    if(this.double_wide) {
-        bucket_list_style = bucket_list_style + " meeting_box_double";
-    }
-
-    var pinned = "";
-    if(this.pinned) {
-        bucket_list_style = bucket_list_style + " meeting_box_pinned";
-        pinned="<td class=\"pinned-tack\">P</td>";
-    }
-
-    var groupacronym = "nogroup";
-    if(this.group != undefined) {
-        groupacronym = this.group.acronym;
-    }
-    //console.log("acronym", groupacronym, this.group.acronym, this.visible_title());
-
-    var durationstring="";
-    if (this.requested_duration!="0.0") {
-         durationstring = " ("+this.requested_duration+")"
-    }
-    // see comment in ietf.ccs, and
-    // http://stackoverflow.com/questions/5148041/does-firefox-support-position-relative-on-table-elements
-    return "<div class='meeting_box_container' session_id=\""+this.session_id+"\"><div class=\"meeting_box "+bucket_list_style+"\" ><table class='meeting_event "+
-        groupacronym +
-        "' id='session_"+
-        this.session_id+
-        "' session_id=\""+this.session_id+"\"" +
-        "><tr id='meeting_event_title'><td class=\"theirconflicts\"></td><th class='"+
-        this.wg_scheme()+" "+
-        this.area_scheme() +" meeting_obj'>"+
-        this.visible_title()+
-        "<span>" + durationstring + "</span>" +
-        "</th><td class=\"ourconflicts\"></td>"+pinned+"</tr></table>"+ area_mark +"</div></div>";
-};
-
-function andthen_alert(object, result, arg) {
-    alert("result: "+result+" on obj: "+object);
-};
-
-Session.prototype.generate_info_table = function() {
-    $("#info_grp").html(name_select_html);
-    if(this.is_bof()) {
-        $("#grp_type").html("BOF");
-    } else {
-        $("#grp_type").html("WG");
-    }
-
-    $("#info_name_select").val($("#info_name_select_option_"+this.session_id).val());
-    if(this.description.length > 33) {
-        $("#info_name").html("<span title=\""+this.description+"\">"+this.description.substring(0,35)+"...</span>");
-    } else {
-        $("#info_name").html(this.description);
-    }
-    $("#info_area").html("<span class='"+this.area.toUpperCase()+"-scheme'>"+this.area+"</span>");
-    $("#info_duration").html(this.requested_duration);
-    if(this.attendees == "None") {
-        $("#info_capacity").text("size unknown");
-    } else {
-        $("#info_capacity").text(this.attendees + " people");
-    }
-
-    if(!read_only) {
-        $("#info_location").html(generate_select_box()+"<button id='info_location_set'>set</button>");
-    }
-    
-    var special_requests_text = '';
-    if(this.joint_with_groups) {
-        special_requests_text += 'Joint session with ' + this.joint_with_groups.join(', ') + '. ';
-    }
-    if(this.constraints.wg_adjacent) {
-        for (var target_href in this.constraints.wg_adjacent) {
-            if (this.constraints.wg_adjacent.hasOwnProperty(target_href)) {
-                special_requests_text += 'Schedule adjacent with ' + this.constraints.wg_adjacent[target_href].othergroup.acronym + '. ';
-            }
-        }
-    }
-    if(this.constraints.time_relation) {
-        special_requests_text += this.constraints.time_relation.time_relation.time_relation_display + '. ';
-    }
-    if(this.constraints.timerange) {
-        special_requests_text += this.constraints.timerange.timerange.timeranges_display + '. ';
-    }
-    if("comments" in this && this.comments.length > 0 && this.comments != "None") {
-        special_requests_text += this.comments;
-    } else {
-        special_requests_text += "Special requests: None";
-    }
-    $("#special_requests").text(special_requests_text);
-
-    this.selectit();
-
-    if(this.slot != undefined) {
-        ss = this.slot;
-        if(ss.timeslot_id == null){
-            $("#info_location_select").val(agenda_globals.meeting_objs[ss.assignment_id]);
-        }else{
-            $("#info_location_select").val(ss.timeslot_id); // ***
-        }
-        $("#info_location_select").val($("#info_location_select_option_"+ss.timeslot_id).val());
-    }
-
-    //console.log("ad for session",this.session_id,"is",this.responsible_ad);
-    if(this.responsible_ad) {
-        this.responsible_ad.populate_responsible_ad();
-    }
-    $("#info_requestedby").html(this.requested_by +" ("+this.requested_time+")");
-
-    listeners();
-};
-
-function load_all_groups() {
-    for(key in agenda_globals.meeting_objs) {
-        session = agenda_globals.meeting_objs[key];
-        session.group = find_group_by_href(session.group_href, "load all");
-    }
-}
-
-var __DEBUG_THIS_SLOT;
-Session.prototype.retrieve_constraints_by_session = function() {
-    __DEBUG_THIS_SLOT = this;
-    //console.log("4 retrieve loaded:", this.title, this.constraints_loaded, "loading:", this.constraints_loading);
-
-    if(this.constraints_promise != undefined) {
-        return this.constraints_promise;
-    }
-
-    var session_obj = this;
-    var href = meeting_base_url+'/session/'+session_obj.session_id+"/constraints.json";
-
-    this.constraints_promise = $.ajax(href);
-    this.constraints_loading = true;
-    this.constraints_promise.success(function(newobj, status, jq) {
-        session_obj.fill_in_constraints(newobj);
-        session_obj.constraints_loaded  = true;
-        session_obj.constraints_loading = false;
-        find_and_populate_conflicts(session_obj);
-    });
-
-    return this.constraints_promise;
-};
-
-var __verbose_person_conflicts = false;
-Session.prototype.calculate_bethere = function() {
-    var session_obj = this;
-
-    if("bethere" in this.constraints) {
-        $.each(this.constraints["bethere"], function(index) {
-            var bethere = session_obj.constraints["bethere"][index];
-            find_person_by_href(bethere.person_href).done(function(person) {
-                if(__verbose_person_conflicts) {
-                    console.log("person",person.ascii,"attends session",session_obj.group.acronym);
-                }
-                person.attend_session(session_obj);
-            });
-        });
-    }
-};
-
-Session.prototype.fill_in_constraints = function(constraint_list) {
-    var session_obj = this;
-    $.each(constraint_list, function(key){
-        thing = constraint_list[key];
-        session_obj.add_constraint_obj(thing);
-    });
-
-    // here we can sort the constraints by group name.
-    // make a single list. this.constraints is not an array, can not use concat.
-    this.conflicts = [];
-    $.each(Object.keys(agenda_globals.group_conflict_labels), (i, conflict_name) => {
-        if (conflict_name in this.constraints) {
-            $.each(this.constraints[conflict_name], (j, conflict) => {
-                session_obj.conflicts.push(conflict);
-            });
-        }
-        if (conflict_name in this.theirconstraints) {
-            $.each(this.theirconstraints[conflict_name], (j, conflict) => {
-                session_obj.conflicts.push(conflict);
-            });
-        }
-    });
-    this.calculate_bethere();
-    this.conflicts = sort_conflict_list(this.conflicts)
-};
-
-// ++++++++++++++++++
-// Group Objects
-function Group() {
-    this.andthen_list = [];      /* should be removed, or replaced with promise */
-    this.all_sessions = [];
-}
-
-Group.prototype.loaded_andthen = function() {
-    me = this;
-    $.each(this.andthen_list, function(index, andthen) {
-        andthen(me);
-    });
-    this.andthen_list = [];
-};
-
-Group.prototype.load_group_obj = function(andthen) {
-    //console.log("group ",this.href);
-    var group_obj = this;
-
-    if(!this.loaded && !this.loading) {
-        this.loading = true;
-        this.andthen_list.push(andthen);
-        $.ajax(this.href,
-               {
-                   success: function(newobj, status, jqXHR) {
-                       if(newobj) {
-                           $.extend(group_obj, newobj);
-                           group_obj.loaded = true;
-                       }
-                       group_obj.loading = false;
-                       group_obj.loaded_andthen();
-                   },
-                   error: function(jqXHR, textStatus, errorThrown ) {
-                       console.log("error loading ",group_obj.href," textStatus: ", textStatus,errorThrown);
-                       group_obj.loading = false;
-                       group_obj.loaded  = true;  // white lie
-                       group_obj.load_error = true;
-                   }
-               });
-    } else {
-        if(!this.loaded) {
-            // queue this continuation for later.
-            this.andthen_list.push(andthen);
-        } else {
-            this.loading = false;
-            andthen(group_obj);
-        }
-    }
-}
-
-Group.prototype.add_session = function(session) {
-    if(this.all_sessions == undefined) {
-        this.all_sessions = [];
-    }
-    this.all_sessions.push(session);
-};
-
-var __DEBUG_GROUP_COLUMN_CLASSES = false;
-Group.prototype.add_column_class = function(column_class) {
-    if(this.column_class_list == undefined) {
-       this.column_class_list = [];
-    }
-    if(__DEBUG_GROUP_COLUMN_CLASSES) {
-        console.log("group",this.acronym,"adding column_class",column_class);
-    }
-    this.column_class_list.push(column_class);
-};
-Group.prototype.del_column_class = function(column_class) {
-    if(__DEBUG_GROUP_COLUMN_CLASSES) {
-        console.log("group",this.acronym,"del column_class",column_class);
-    }
-    for(n in this.column_class_list) {
-        if(this.column_class_list[n] == column_class) {
-            this.column_class_list.splice(n,1);
-        }
-    }
-};
-
-Group.prototype.add_column_classes = function(column_class_list) {
-    for(ccn in column_class_list) {
-        cc = column_class_list[ccn];
-        this.add_column_class(cc);
-    }
-};
-Group.prototype.del_column_classes = function(column_class_list) {
-    for(ccn in column_class_list) {
-        cc = column_class_list[ccn];
-        this.del_column_class(cc);
-    }
-};
-
-var __debug_group_load = false;
-function create_group_by_href(href) {
-    if(agenda_globals.group_objs[href] == undefined) {
-       agenda_globals.group_objs[href]=new Group();
-        var g = agenda_globals.group_objs[href];
-        if(__debug_group_load) {
-            console.log("creating new group for", href);
-        }
-        g.loaded = false;
-        g.loading= false;
-    }
-    return agenda_globals.group_objs[href];
-}
-
-function load_group_by_href(href) {
-    var g = agenda_globals.group_objs[href];
-    if(!g.loaded) {
-        g.href = href;
-        if(__debug_group_load) {
-            console.log("loading group href", href);
-        }
-        g.load_group_obj(function() {});
-    }
-    return g;
-}
-
-// takes a json that has at least a "href" member,
-// and finds or creates the object.  Any additional
-// fields are added to the group object, and the group
-// is marked loaded.  The resulting group object is returned.
-function load_group_from_json(json) {
-    var g = create_group_by_href(json.href);
-    for(var key in json) {
-        if(json[key].length > 0) {
-            g[key]=json[key];
-        }
-    }
-    g.loaded = true;
-    g.loading= false;
-    return g;
-}
-
-var group_references   = 0;
-var group_demand_loads = 0;
-function find_group_by_href(href, msg) {
-    group_references++;
-    var g=agenda_globals.group_objs[href];
-    if(g == undefined) {
-        group_demand_loads++;
-        if(__debug_group_load) {
-            console.log("loading",href,"because of",msg);
-        }
-        g = create_group_by_href(href);
-        load_group_by_href(href);
-    }
-    //console.log("finding group by ", href, "gives: ", g);
-    return g;
-}
-
-// ++++++++++++++++++
-// Constraint Objects
-function Constraint() {
-// fields: (see ietf.meeting.models Constraint.json_dict)
-//
-//  -constraint_id
-//  -href
-//  -name             -- really the conflict_type, which will get filled in
-//  -person/_href
-//  -source/_href
-//  -target/_href
-//  -meeting/_href
-//
-}
-
-var conflict_classes = {};
-
-function clear_conflict_classes() {
-    // remove all conflict boxes from before
-    $.each(Object.keys(agenda_globals.group_conflict_labels), (i, conflict_name) => {
-        $(".show_" + conflict_name + "_specific_box").removeClass("show_" + conflict_name + "_specific_box");
-    });
-
-    // reset all column headings
-    $(".show_conflict_view_highlight").removeClass("show_conflict_view_highlight");
-}
-function find_conflict(domid) {
-    return conflict_classes[domid];
-}
-
-Constraint.prototype.column_class_list = function() {
-    return this.othergroup.column_class_list;
-};
-
-/* N.B., handling new conflict types as equivalent to the originals for prioritization.
- * If this editor is not replaced by the new one, it might be worth sorting out how to
- * properly prioritize. Otherwise, this maintains the behavior seen prior to the introduction
- * of the new conflicts. */
-Constraint.prototype.conflict1P = function() {
-    return ((this.conflict_type === "conflict") || (this.conflict_type === "chair_conflict"));
-};
-
-Constraint.prototype.conflict2P = function() {
-    return ((this.conflict_type === "conflic2") || (this.conflict_type === "tech_overlap"));
-};
-
-Constraint.prototype.conflict3P = function() {
-    return ((this.conflict_type === "conflic3") || (this.conflict_type === "key_participant"));
-};
-
-Constraint.prototype.conflict_groupP = function() {
-    return this.conflict_type in agenda_globals.group_conflict_labels;
-};
-
-Constraint.prototype.conflict_peopleP = function() {
-    return (this.conflict_type === "bethere")
-};
-
-Constraint.prototype.conflict_compare = function(oflict) {
-    if(this.conflict_peopleP()) {
-        return oflict;
-    }
-    if(this.conflict1P()) {
-        /* "conflict" is highest, return it. */
-        return this;
-    }
-    if(this.conflict2P() && oflict.conflict3P()) {
-        /* "conflic2" > "conflic3" */
-        return this;
-    }
-    /* self > 2, so otype would win */
-    return oflict;
-};
-
-// red is arbitrary here... There should be multiple shades of red for
-// multiple types of conflicts.
-
-
-
-var __CONSTRAINT_DEBUG = null;
-var __column_class_debug = false;
-
-// one used to get here by having the conflict boxes enabled/disabled, but they were
-// removed from the UI.
-// when a session is selected, the conflict boxes are filled in,
-// and then they are all clicked in order to highlight everything.
-Constraint.prototype.show_conflict_view = function() {
-    classes=this.column_class_list();
-    if(classes == undefined) {
-        classes = []
-    }
-    //console.log("show_conflict_view", this);
-    __CONSTRAINT_DEBUG = this;
-    if(__column_class_debug) {
-        console.log("show conflict", this.href, "classes", classes.length, this);
-    }
-
-    // this highlights the column headings of the sessions that conflict.
-    for(ccn in classes) {
-       var cc = classes[ccn];   // cc is a ColumnClass now
-
-        if(cc != undefined) {
-            /* this extracts the day from this structure */
-            var th_tag = cc.th_tag;
-            if(__column_class_debug) {
-                console.log("add conflict for column_class", this.session.title, th_tag);
-            }
-            $(th_tag).addClass("show_conflict_view_highlight");
-        } else {
-            console.log("cc is undefined for ccn:",ccn);
-        }
-    }
-
-    // this highlights the conflicts themselves
-    //console.log("make box", this.thisgroup.href);
-    sessions = this.othergroup.all_sessions
-    // set class to like:  .show_conflict_specific_box
-    conflict_class = "show_"+this.conflict_type+"_specific_box";
-    if(sessions) {
-      $.each(sessions, function(key) {
-          //console.log("2 make box", key);
-          this.element().addClass(conflict_class);
-      });
-    }
-    //console.log("viewed", this.thisgroup.href);
-};
-
-Constraint.prototype.populate_conflict_classes = function() {
-    // this is used for the red square highlighting.
-    var checkbox_id = "conflict_"+this.dom_id;
-    conflict_classes[checkbox_id] = this;
-};
-
-// Made dead by change to how the group view is built out
-//Constraint.prototype.build_group_conflict_view = function() {
-//
-//    var display = { 'conflict':'1' , 'conflic2':'2' , 'conflic3':'3' };
-//
-//    // this is used for the red square highlighting.
-//    var checkbox_id = "conflict_"+this.dom_id;
-//    conflict_classes[checkbox_id] = this;
-//
-//    build  = "<div id='"+this.dom_id+"'>";
-//    if (this.direction=='theirs') {
-//        build += display[this.conflict_type]+"->";
-//    }
-//    build += this.othergroup_name;
-//    if (this.direction=='ours') {
-//        build += "->"+display[this.conflict_type];
-//    }
-//    build += "</div>";
-//
-//    return build
-//
-//};
-
-Constraint.prototype.build_people_conflict_view = function() {
-    var area_mark =  "";
-    if(this.person != undefined && this.person.area_mark_basic != undefined) {
-        area_mark = this.person.area_mark_basic;
-    }
-    return "<div class='conflict our-"+this.conflict_type+"' id='"+this.dom_id+
-           "'>"+area_mark+"</div>";
-};
-
-Constraint.prototype.build_othername = function() {
-    if(this.othergroup.load_error) {
-        console.log("request for unloaded group: ",this.othergroup.href);
-        var patt = /.*\/group\//;  // ugly assumption about href structure.
-        var base = this.othergroup.href.replace(patt,"")
-        this.othergroup_name = base.replace(".json","")
-    } else {
-        this.othergroup_name = this.othergroup.acronym;
-    }
-};
-
-// subclasses would make some sense here.
-Constraint.prototype.conflict_view = function() {
-    this.dom_id = "constraint_"+this.constraint_id;
-
-    if(this.conflict_peopleP()) {
-        return this.build_people_conflict_view();
-    }
-    else {
-        // This function is currently never called for this case
-        console.log("!! unexpected conflict_view for", this.href);
-        //this.build_othername();
-        //return this.build_group_conflict_view();
-    }
-};
-
-var _constraint_load_debug = false;
-// SESSION CONFLICT OBJECTS
-// take an object and add attributes so that it becomes a session_conflict_obj.
-// note that constraints are duplicated: each session has both incoming and outgoing constraints added.
-Session.prototype.add_constraint_obj = function(obj) {
-    // turn this into a Constraint object
-    // can not print or JSONify these on ff as this has cyclic references. Chrome can.
-    //console.log("session: ",       this);
-    //console.log("add_constraint: ",obj.constraint_id, obj.name);
-
-    obj2 = new Constraint();
-    $.extend(obj2, obj);
-
-    obj = obj2;
-    obj.session   = this;
-
-    var listname = obj.name;
-    obj.conflict_type = listname;
-    if(this.constraints[listname]==undefined) {
-       this.constraints[listname]={};
-    }
-    if(this.theirconstraints[listname]==undefined) {
-       this.theirconstraints[listname]={};
-    }
-
-    if(listname == "bethere") {
-        //console.log("bethere constraint: ", obj);
-        var person_href = obj.person_href;
-        var session     = this;
-        this.constraints[listname][person_href]=obj;
-        find_person_by_href(person_href).done(function(person) {
-            if(_constraint_load_debug) {
-                console.log("recorded bethere constraint: ",person.ascii,"for group",session.group.acronym);
-            }
-            obj.person = person;
-        });
-    } else {
-        // must be conflic*, timerange, time_relation or wg_adjacent
-        var ogroupname;
-        if(obj.source_href == this.group_href) {
-            obj.thisgroup  = this.group;
-            obj.othergroup = find_group_by_href(obj.target_href, "constraint src"+obj.href);
-            obj.direction = 'ours';
-            if (obj.target_href) {
-                ogroupname = obj.target_href;
-            } else {
-                ogroupname = obj.name;
-            }
-            if(this.constraints[listname][ogroupname]) {
-                console.log("Found multiple instances of",this.group_href,listname,ogroupname);
-            }
-            this.constraints[listname][ogroupname] = obj
-        } else {
-            obj.thisgroup  = this.group;
-            obj.othergroup = find_group_by_href(obj.source_href, "constraint dst"+obj.href);
-            obj.direction = 'theirs';
-            ogroupname = obj.source_href;
-            if(this.theirconstraints[listname][ogroupname]) {
-                console.log("Found multiple instances of",ogroupname,listname,this.group_href);
-            }
-            this.theirconstraints[listname][ogroupname] = obj
-        }
-
-    }
-
-};
-
-function constraint_compare(a, b)
-{
-    if(a==undefined || a.othergroup == undefined) {
-       return -1;
-    }
-    if(b==undefined || b.othergroup == undefined) {
-       return 1;
-    }
-    return (a.othergroup.href > b.othergroup.href ? 1 : -1);
-}
-
-function sort_conflict_list(things) {
-    var keys = Object.keys(things);
-    var keys1 = keys.sort(function(a,b) {
-                                 return constraint_compare(things[a],things[b]);
-                             });
-    var newlist = [];
-    for(i=0; i<keys1.length; i++) {
-       var key  = keys1[i];
-       newlist[i] = things[key];
-    }
-    return newlist;
-}
-
-// ++++++++++++++
-// Person Objects
-function Person() {
-// fields: (see ietf.person.models Person.json_dict)
-//
-//
-}
-
-var all_people = {};
-
-// this should be done completely with promises, but
-// one can't use the promise returned by $.ajax() directly,
-// because the object it returns does not seem to persist.
-// So, one has to create a new persistent object.
-// This returns a promise that will receive the person object.
-function find_person_by_href(href) {
-
-    // first, look and see if the a promise for this href
-    // already.
-
-    var deferal;
-    if(!(href in all_people)) {
-        deferal = $.Deferred();
-        all_people[href]= deferal;
-
-        var lookup = $.ajax(href);
-        lookup.success(function(newobj, status, jqXHR) {
-	    person = new Person();
-	    $.extend(person, newobj);
-            person.loaded = true;
-            deferal.resolve(person);
-        });
-    } else {
-        deferal = all_people[href];
-    }
-
-    // or should this be deferal.promise()?
-    return deferal.promise();
-}
-
-var area_result;
-// this function creates a unique per-area director mark
-function mark_area_directors(directorpromises) {
-    $.each(area_directors, function(areaname) {
-        var adnum = 1;
-        $.each(area_directors[areaname], function(key) {
-            var thisad = adnum;
-            directorpromises.push(this);
-            this.done(function(person) {
-                person.make_area_mark(areaname, thisad);
-                person.marked = true;
-            });
-            adnum++;
-        });
-    });
-    return directorpromises;
-}
-
-Person.prototype.make_area_mark = function(areaname, adnum) {
-    this.area_mark =  "<span title=\""+areaname+" "+this.name+"\" class=\" director-rightup\">";
-    this.area_mark += "<span class=\"personconflict hidepersonconflict\">AD</span>";
-    this.area_mark += "<span class=\"director-mark-" + areaname.toUpperCase() + " director-mark\">";
-    this.area_mark += adnum;
-    this.area_mark += "</span></span>";
-
-    this.area_mark_basic =  "<span title=\""+areaname+" "+this.name+"\" class=\"director-mark-" + areaname.toUpperCase() + " director-mark director-right\">";
-    this.area_mark_basic += adnum;
-    this.area_mark_basic += "</span>";
-};
-
-Person.prototype.populate_responsible_ad = function() {
-    var area_mark = "";
-    if(this.area_mark != undefined) {
-        area_mark = this.area_mark_basic;
-    }
-    $("#info_responsible").html(this.name + area_mark);
-};
-
-var _person_bethere_debug = false;
-// this marks a person as needing to attend a session
-// in a particular timeslot.
-Person.prototype.clear_session = function(session, old_column_classes) {
-    for(ccn in old_column_class_list) {
-        var vertical_location = session.column_class_list[ccn].column_tag;
-        var room_tag          = session.column_class_list[ccn].room_tag;
-
-        if(_person_bethere_debug) {
-            console.log("person",this.ascii,"maybe no longer attending session",
-                        session.session_id, "in room",room_tag);
-        }
-
-        // probably should make it dict, to make removal easier
-        if(this.sessions == undefined) {
-            continue;
-        }
-
-        if(this.sessions[vertical_location] == undefined) {
-            continue;
-        }
-
-        if(room_tag in this.sessions[vertical_location]) {
-            delete this.sessions[vertical_location][room_tag];
-            if(_person_bethere_debug) {
-                console.log("person: ",this.ascii,"removed from room",
-                            room_tag, "at", vertical_location);
-            }
-        }
-    }
-};
-
-Person.prototype.attend_session = function(session) {
-    for(ccn in session.column_class_list) {
-        var vertical_location = session.column_class_list[ccn].column_tag;
-        var room_tag          = session.column_class_list[ccn].room_tag;
-
-        if(_person_bethere_debug) {
-            console.log("person",this.ascii,"maybe attending session", session.session_id, "in room",room_tag);
-        }
-
-        // probably should make it dict, to make removal easier
-        if(this.sessions == undefined) {
-            this.sessions = [];
-        }
-        if(this.sessions[vertical_location] == undefined) {
-            this.sessions[vertical_location] = [];
-        }
-
-
-        if(!(room_tag in this.sessions[vertical_location])) {
-            this.sessions[vertical_location][room_tag]=true;
-            if(_person_bethere_debug) {
-                console.log("person: ",this.ascii,"needs to be in room",
-                            room_tag, "at", vertical_location);
-            }
-        }
-    }
-};
-
-Person.prototype.conflicted_time = function(vertical_location) {
-    var yesno = this.conflicted_time1(vertical_location);
-    //console.log("person: ",this.ascii,"examining for", vertical_location, "gives",yesno);
-    return yesno;
-}
-
-Person.prototype.conflicted_time1 = function(vertical_location) {
-    if(this.sessions == undefined) {
-        return false;
-    }
-
-    if(this.sessions[vertical_location] == undefined) {
-        return false;
-    }
-
-    var placestobe = Object.keys(this.sessions[vertical_location]);
-    if(placestobe.length > 1) {
-        return true;
-    } else {
-        return false;
-    }
-};
-
-
-/*
- * Local Variables:
- * c-basic-offset:4
- * End:
- */
diff --git a/ietf/templates/meeting/edit_meeting_schedule.html b/ietf/templates/meeting/edit_meeting_schedule.html
index e834215d0..5d3c60271 100644
--- a/ietf/templates/meeting/edit_meeting_schedule.html
+++ b/ietf/templates/meeting/edit_meeting_schedule.html
@@ -49,7 +49,7 @@
         &middot;
       {% endif %}
 
-      <a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">New agenda</a>
+      <a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">Copy agenda</a>
       &middot;
 
       <a href="{% url "ietf.meeting.views.list_schedules" num=meeting.number %}">Other Agendas</a>
diff --git a/ietf/templates/meeting/landscape_edit.html b/ietf/templates/meeting/landscape_edit.html
deleted file mode 100644
index 85e0ef9d6..000000000
--- a/ietf/templates/meeting/landscape_edit.html
+++ /dev/null
@@ -1,388 +0,0 @@
-{% extends "base.html" %}
-{# Copyright The IETF Trust 2015, All Rights Reserved #}
-{% load origin %}
-{% load static %}
-{% load ietf_filters %}
-{% load humanize %}
-
-{% 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 }} }
-.director-mark-{{ area.upcase_acronym}} {
-   border: 2px solid {{ area.fg_color}};
-   color:{{ area.fg_color }};
-   background-color: {{ area.bg_color }}
-}
-{% endfor %}
-{% endblock morecss %}
-
-{% block title %}IETF {{ meeting.number }} Meeting Agenda{% endblock %}
-{% load agenda_custom_tags %}
-{% block pagehead %}
-<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/jquery-ui-themes/jquery-ui-1.8.11.custom.css' %}" />
-<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/agenda.css' %}" />
-{% endblock pagehead %}
-
-{% block js %}
-<script type="text/javascript" src="{% static 'ietf/js/agenda/jquery-1.8.2.min.js' %}"></script>
-<script src="{% static 'js-cookie/src/js.cookie.js' %}"></script>
-<script>
-jQuery.ajaxSetup({
-    crossDomain: false, // obviates need for sameOrigin test
-    beforeSend: function(xhr, settings) {
-        if (!csrfSafeMethod(settings.type)) {
-            xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
-        }
-    }
-});
-</script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/minified/jquery-ui.custom.min.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/minified/jquery.ui.widget.min.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/minified/jquery.ui.droppable.min.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/minified/jquery.ui.sortable.min.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/minified/jquery.ui.accordion.min.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/minified/jquery.ui.draggable.min.js' %}"></script>
-
-<script type='text/javascript' src="{% static 'spin.js/spin.min.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_edit.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_helpers.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_objects.js' %}"></script>
-<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_listeners.js' %}"></script>
-
-
-<script type='text/javascript'>
-
-
-var meeting_number = "{{ meeting.number }}";
-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 assignments_post_href = "{% url "ietf.meeting.ajax.assignments_json" meeting.number schedule.owner_email schedule.name %}";
-var total_days = {{time_slices|length}};
-var total_rooms = {{rooms|length}};
-
-function setup_slots(promiselist){
-{% for day in time_slices %}
-    days.push("{{day}}");
-{% endfor %}
-
-{% for ad in area_directors %}
-area_directors["{{ad.group.acronym}}"] = [];
-{% endfor %}
-{% for ad in area_directors %}
-area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.defurl}}"));
-{% endfor %}
-
-var ts_promise = load_timeslots("{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}");
-var sess_promise = load_sessions("{% url "ietf.meeting.ajax.sessions_json" meeting.number %}");
-promiselist.push(ts_promise);
-promiselist.push(sess_promise);
-
-var ss_promise = load_assignments(ts_promise, sess_promise, assignments_post_href);
-promiselist.push(ss_promise);
-
-   console.log("setup_slots run");
-
-{% for area in area_list %}
-   legend_status["{{area.upcase_acronym}}"] = true;
-{% endfor %}
-
-}
-
-
-
-
-
-</script>
-<style type='text/css'>
-
-</style>
-{% endblock js %}
-
-
-{% block content %}
-  {% origin %}
-<div id="read_only">
-  <p>You do not have write permission to agenda: {{schedule.name}}</p>
-  {% if schedule.is_official_record %}
-  <p>This is the official schedule for a meeting in the past.</p>
-  {% endif %}
-  <p>Please save this agenda to your account first.</p>
-</div>
-
-<div class="content"> {% comment %} For preserving styling across the facelift {% endcomment %}
-
-<div class="wrapper custom_text_stuff">
-
-
-<div id="unassigned-items">
-  <div id="all_agendas" class="events_bar_buttons">
-    <a href="{% url "ietf.meeting.views.list_schedules" meeting.number %}">
-      <button class="styled_button">all agendas</button>
-    </a>
-  </div>
-  <div id="hidden_room" class="hide_buttons events_bar_buttons">
-    <div class="very_small left">hidden rooms:<span id="hidden_rooms" >0/{{rooms|length}}</span></div>
-    <div><button class="small_button" id="show_hidden_rooms">Show</button></div>
-  </div>
-  <div id="hidden_day" class="hide_buttons events_bar_buttons">
-    <div class="very_small left">hidden days:<span id="hidden_days" >0/{{time_slices|length}}</span></div>
-    <div><button class="small_button" id="show_hidden_days">Show</button></div>
-  </div>
-
-  </div>
-
-  <div id="unassigned_order" class="events_bar_buttons">
-    <select id="unassigned_sort_button" class="dialog">
-      <option id="unassigned_alpha" value="alphaname" selected>Alphabetical</option>
-      <option id="unassigned_area" value="area">By Area</option>
-      <option id="unassigned_duration" value="duration">By Duration</option>
-      <option id="unassigned_special" value="special">Special Request</option>
-    </select>
-  </div>
-  <div class="agenda_slot_title" >
-    <div style="ui-icon ui-icon-arrow-1-w" id="close_ietf_menubar">
-      &lt;
-    </div>
-    <b>Unassigned Events:</b>
-    <span id="schedule_name">name: {{schedule.name}}</span>
-  </div>
-  <div id="sortable-list" class="ui-droppable bucket-list room_title">
-  </div>
-</div>
-
-<div class="agenda_div">
-
-<div id="dialog-confirm" title="" style="display:none">
-  <p>
-    <span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
-    Are you sure you want to put two sessions into the same slot?
-  </p>
-</div>
-
-<div id="can-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
-</div>
-
-<div id="can-not-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
-  <p>
-    <span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
-    You can not extend this session. The slot is not available.
-  </p>
-</div>
-
-
-<div id="dialog-confirm" title="" style="display:none">
-  <p>
-    <span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
-    Are you sure you want to put two sessions into the same slot?
-  </p>
-</div>
-
-<!-- some boxes for dialogues -->
-<div id="dialog-confirm-two" title="" style="display:none">
-  <p>
-    <span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
-    <span class="dialog-confirm-text">Are you sure you want to put two sessions into the same slot?</span>
-  </p>
-</div>
-
-<div id="dialog-confirm-toosmall" title="" style="display:none">
-  <p>
-    <span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
-    <span class="dialog-confirm-text">The room you are moving to has a lower
-      room capacity then the requested capacity,<br>
-      Are you sure you want to continue?
-    </span>
-  </p>
-</div>
-
-<div id="dialog-confirm-twotoosmall" title="" style="display:none">
-  <p>
-    <span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
-    <span class="dialog-confirm-text">
-      The slot you are moving to already has a session in it, <br>
-      the room is also smaller than the requested amount.<br>
-      Are you sure you want to continue?
-    </span>
-  </p>
-</div>
-
-
-{%comment%}<table id="meetings" class="ietf-navbar" style="width:100%">{%endcomment%}
-<table id="meetings" class="ietf-navbar" >
-<tr>
-  <th class="schedule_title"><div id="pageloaded" style="display:none">loaded</div><div id="spinner"><!-- spinney goes here --></div></th>
-  {% comment %}<th></th>{% endcomment %}
-{% for day in time_slices %}
-  <th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title agenda_slot_unavailable">
-    <div id="close_{{day|date:'Y-m-d'}}" class="close top_left very_small close_day">x</div>
-    {{day|date:'D'}}&nbsp;({{day}})
-
-  </th>
-  <th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
-    <div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
-      <div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
-    </div>
-  </th>
-{% endfor %}
-</tr>
-
-<tr>
-      <th class="th_column"><button id="show_all_button" class="styled_button">show all</button></th>
-      {% comment %}<th><!-- resources --></th>{% endcomment %}
-    {% for day in time_slices %}
-	  {% for slot in date_slices|lookup:day %}
-	      <th class="day_{{day}}-{{slot.0|date:'Hi'}} day_{{day}} room_title ">{{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}} </th>
-
-          {% endfor %}
-              <th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></th>
-
-    {% endfor %}
-</tr>
-
-
-
-
-  {% for r in rooms %}
-  <tr id="{{r.name|to_acceptable_id}}" class="{% cycle 'agenda_row_alt' '' %} agenda_slot">
-    <th class="vert_time">
-      <div class="close very_small close_room top_left small_button" id="close_{{r.name|to_acceptable_id}}">X</div>
-      <div class="right room_name">{{r.name}} <span class="capacity">({{r.capacity}})</span></div>
-    </th>
-{% comment 'The secretariat is not using these features' %}
-    <th class="room_features">
-      <div class="resource_list">
-      {% for resource in r.resources.all %}
-      <span class="resource_image">
-      <img src="{% static 'ietf/images/{{ resource.icon }}' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>
-      </span>
-      {% endfor %}
-      </div>
-    </th>
-{% endcomment %}
-    {% for day in time_slices %}
-      {% for slot in date_slices|lookup:day %}
-        <td id="{{r.dom_id}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot agenda_slot_unavailable" capacity="{{r.capacity}}" ></td>
-      {% endfor %}
-        <td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
-    {% endfor %}
-  </tr>
-  {% endfor %}
-</table>
-
-
-
-</div>
-
-
-<div id="session-info" class="ui-droppable bucket-list room_title">
-  <div class="agenda_slot_title"><b>Session Information:</b></div>
-
-  <div class="ss_info_box">
-    <div class="ss_info ss_info_left">
-      <table>
-	<tr><td class="ss_info_name_short">Group:</td><td><span id="info_grp"></span>
-            <!-- <button id="agenda_sreq_button" class="right">Edit Request</button> --></tr>
-	<tr><td class="ss_info_name_short">Name:</td> <td id="info_name"></td></tr>
-	<tr><td class="ss_info_name_short">Area:</td> <td><span id="info_area"></span>
-            <button id="show_all_area" class="right">Show All</button></td></tr>
-        <tr>
-          <td colspan=2>
-            <div class="agenda_nice_button" id="agenda_find_free">
-              <button class="agenda_selected_buttons small_button" id="find_free">Find Free</button>
-            </div>
-            <div class="agenda_nice_button button_disabled" id="agenda_double_slot">
-              <button class="agenda_selected_buttons small_button" disabled id="double_slot">Extend</button>
-            </div>
-            <div id="agenda_pin_slot" class="agenda_nice_button button_disabled">
-              <button class="agenda_selected_buttons small_button" disabled id="pin_slot">Pin</button>
-            </div>
-          </td>
-        </tr>
-
-      </table>
-    </div>
-
-    <div class="ss_info ss_info_right">
-      <table>
-	<tr><td class="ss_info_name_long">Duration/Capacity:</td>
-          <td class="info_split"><span id="info_duration"></span>
-            <span style="right"	id="grp_type"></span></td>
-          <td class="info_split" id="info_capacity"></td></tr>
-	<tr><td class="ss_info_name_long">Location:</td><td colspan=2 id="info_location"></td></tr>
-	<tr><td class="ss_info_name_long">Responsible AD:</td><td colspan=2 id="info_responsible"></td></tr>
-	<tr><td class="ss_info_name_long">Requested By:</td><td colspan=2 id="info_requestedby"></td></tr>
-        <tr>
-          <td colspan=3>
-            <div class="agenda_nice_button button_disabled" id="agenda_prev_session">
-              <button class="agenda_selected_buttons small_button" disabled id="prev_session">Prev</button>
-            </div>
-            <div class="agenda_nice_button button_disabled" id="agenda_show">
-              <button class="agenda_selected_buttons small_button" disabled id="show_session">Show</button>
-            </div>
-            <div class="agenda_nice_button button_disabled" id="agenda_next_session">
-              <button class="agenda_selected_buttons small_button" disabled id="next_session">Next</button>
-            </div>
-
-            <div class="request_features" id="agenda_requested_features">
-
-            </div>
-          </td>
-        </tr>
-      </table>
-    </div>
-
-    <div id="conflict_table">
-      <div id="special_requests">Special Requests</div>
-      <table>
-	<tbody id="conflict_table_body">
-	  <tr class="conflict_list_row">
-            <td class="conflict_list_title">
-              Group conflicts
-            </td>
-	    <td id="conflict_group_list">
-            <ul>
-            </ul>
-            </td>
-          </tr>
-	  <tr class="conflict_list_row">
-            <td class="conflict_list_title">
-              <b>be present</b>
-            </td>
-	    <td id="conflict_people_list">
-            <ul>
-            </ul>
-            </td>
-          </tr>
-	</tbody>
-      </table>
-    </div>
-    <div class="color_legend">
-      {% for area in area_list %}
-      <span class="{{area.upcase_acronym}}-scheme"><input class='color_checkboxes' type="checkbox" id="{{area.upcase_acronym}}" value="{{area.upcase_acronym}}-value" checked>{{area.upcase_acronym}}</span>
-      {% endfor %}
-    </div>
-  </div>
-
-  <div class="agenda_save_box">
-
-    <div id="agenda_title"><b>Agenda name: </b><span>{{schedule.name}}</span></div>
-    {% if can_edit_properties %}
-      <div><b>Properties</b> <a href="{% url "ietf.meeting.views.edit_schedule_properties" schedule.meeting.number schedule.owner_email schedule.name %}">Edit</a></div>
-    {% endif %}
-    <div id="agenda_saveas">
-      <form action="{{saveasurl}}" method="post">{% csrf_token %}
-        {{ saveas.as_p }}
-        <input id="saveasbutton" type="submit" name="saveas" value="saveas">
-      </form>
-    </div>
-  </div>
-
-</div>
-
-</div> {% comment %} End of .content div {% endcomment %}
-
-{% endblock %}
diff --git a/ietf/templates/meeting/schedule_list.html b/ietf/templates/meeting/schedule_list.html
index a51305431..c482c0936 100644
--- a/ietf/templates/meeting/schedule_list.html
+++ b/ietf/templates/meeting/schedule_list.html
@@ -38,12 +38,12 @@
             {% for schedule in schedules %}
               <tr>
                 <td>
-                  <a href="{% url "ietf.meeting.views.edit_schedule" meeting.number schedule.owner_email schedule.name %}" title="Show regular sessions in agenda">{{ schedule.name }}</a>
+                  <a href="{% url "ietf.meeting.views.edit_meeting_schedule" meeting.number schedule.owner_email schedule.name %}" title="Show regular sessions in agenda">{{ schedule.name }}</a>
                 </td>
                 <td>{{ schedule.owner }}</td>
                 <td>
                   {% if schedule.origin %}
-                    <a href="{% url "ietf.meeting.views.edit_schedule" meeting.number schedule.origin.owner_email schedule.origin.name %}">{{ schedule.origin.name }}</a>
+                    <a href="{% url "ietf.meeting.views.edit_meeting_schedule" meeting.number schedule.origin.owner_email schedule.origin.name %}">{{ schedule.origin.name }}</a>
                     <a href="{% url "ietf.meeting.views.diff_schedules" meeting.number %}?from_schedule={{ schedule.origin.name|urlencode }}&to_schedule={{ schedule.name|urlencode }}" title="{{ schedule.changes_from_origin }} change{{ schedule.changes_from_origin|pluralize }} from {{ schedule.origin.name }}">+{{ schedule.changes_from_origin }}</a>
                   {% endif %}
                 </td>