From bf45f8f4d05d9842b285013cfcb0a0235ce51c5d Mon Sep 17 00:00:00 2001 From: Parker Berberian Date: Tue, 2 Jul 2019 17:33:09 -0400 Subject: Hacking on AJAX This change incorporates the multiple changes we've made in the past few days in order to actually get AJAX working. Change-Id: Ia61a0395e7dbac26a143ca6e15049e6a4bdc8b0d Signed-off-by: Parker Berberian Signed-off-by: Sawyer Bergeron --- src/dashboard/views.py | 39 +-- src/static/js/dashboard.js | 156 +++++++++++- src/templates/base.html | 35 +-- src/templates/dashboard/genericselect.html | 21 +- src/templates/dashboard/landing.html | 46 +--- .../dashboard/multiple_select_filter_widget.html | 3 - .../dashboard/searchable_select_multiple.html | 2 - src/templates/resource/steps/pod_definition.html | 3 - src/templates/workflow/confirm.html | 97 +------- src/templates/workflow/viewport-base.html | 268 +-------------------- src/templates/workflow/viewport-element.html | 36 --- src/workflow/models.py | 6 +- src/workflow/urls.py | 6 +- src/workflow/views.py | 59 ++--- src/workflow/workflow_manager.py | 36 ++- 15 files changed, 243 insertions(+), 570 deletions(-) (limited to 'src') diff --git a/src/dashboard/views.py b/src/dashboard/views.py index aaad7ab..9416cb4 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -63,36 +63,15 @@ def host_profile_detail_view(request): def landing_view(request): - manager = None - manager_detected = False - if 'manager_session' in request.session: - - try: - manager = ManagerTracker.managers[request.session['manager_session']] - - except KeyError: - pass - - if manager is not None: - # no manager detected, don't display continue button - manager_detected = True - - if request.method == 'GET': - return render(request, 'dashboard/landing.html', {'manager': manager_detected, 'title': "Welcome to the Lab as a Service Dashboard"}) - - if request.method == 'POST': - try: - create = request.POST['create'] - - if manager is not None: - del manager - - mgr_uuid = create_session(create, request=request,) - request.session['manager_session'] = mgr_uuid - return HttpResponseRedirect('/wf/') - - except KeyError: - pass + manager = ManagerTracker.managers.get(request.session.get('manager_session')) + return render( + request, + 'dashboard/landing.html', + { + 'manager': manager is not None, + 'title': "Welcome to the Lab as a Service Dashboard" + } + ) class LandingView(TemplateView): diff --git a/src/static/js/dashboard.js b/src/static/js/dashboard.js index f1eff77..7f8a427 100644 --- a/src/static/js/dashboard.js +++ b/src/static/js/dashboard.js @@ -4,14 +4,107 @@ form_submission_callbacks = []; //all runnables will be executed before form submission - /////////////////// // Global Functions /////////////////// +function update_page(response) { + if( response.redirect ) + { + window.location.replace(response.redirect); + return; + } + draw_breadcrumbs(response.meta); + update_exit_button(response.meta); + update_side_buttons(response.meta); + $("#formContainer").html(response.content); +} + +function update_side_buttons(meta) { + const step = meta.active; + const page_count = meta.steps.length; + + const back_button = document.getElementById("gob"); + if (step == 0) { + back_button.classList.add("disabled"); + back_button.disabled = true; + } else { + back_button.classList.remove("disabled"); + back_button.disabled = false; + } + + const forward_btn = document.getElementById("gof"); + if (step == page_count - 1) { + forward_btn.classList.add("disabled"); + forward_btn.disabled = true; + } else { + forward_btn.classList.remove("disabled"); + forward_btn.disabled = false; + } +} + +function update_exit_button(meta) { + if (meta.workflow_count == 1) { + document.getElementById("cancel_btn").innerText = "Exit Workflow"; + } else { + document.getElementById("cancel_btn").innerText = "Return to Parent"; + } +} + +function draw_breadcrumbs(meta) { + $("#topPagination").children().not(".page-control").remove(); + + for (const i in meta.steps) { + const step_btn = create_step(meta.steps[i], i == meta["active"]); + $("#topPagination li:last-child").before(step_btn); + } +} + +function create_step(step_json, active) { + const step_dom = document.createElement("li"); + // First create the dom object depending on active or not + step_dom.className = "topcrumb"; + if (active) { + step_dom.classList.add("active"); + } + $(step_dom).html(`${step_json['title']}`) + + const code = step_json.valid; + + let stat = ""; + let msg = ""; + if (code < 100) { + $(step_dom).children().first().append("") + stat = ""; + msg = ""; + } else if (code < 200) { + $(step_dom).children().first().append("") + stat = "invalid"; + msg = step_json.message; + } else if (code < 300) { + $(step_dom).children().first().append("") + stat = "valid"; + msg = step_json.message; + } + + if (step_json.enabled == false) { + step_dom.classList.add("disabled"); + } + if (active) { + update_message(msg, stat); + } + + return step_dom; +} + +function update_description(title, desc) { + document.getElementById("view_title").innerText = title; + document.getElementById("view_desc").innerText = desc; +} -function updatePage(data){ - updateBreadcrumbs(data['meta']); - $("formContainer").html(data['content']); +function update_message(message, stepstatus) { + document.getElementById("view_message").innerText = message; + document.getElementById("view_message").className = "step_message"; + document.getElementById("view_message").classList.add("message_" + stepstatus); } function submitStepForm(next_step = "current"){ @@ -22,11 +115,10 @@ function submitStepForm(next_step = "current"){ "step_form": step_form_data, "csrfmiddlewaretoken": $("[name=csrfmiddlewaretoken]").val() }); - console.log(form_data); $.post( '/workflow/manager/', form_data, - (data) => updatePage(data), + (data) => update_page(data), 'json' ).fail(() => alert("failure")); } @@ -37,6 +129,58 @@ function run_form_callbacks(){ form_submission_callbacks = []; } +function create_workflow(type) { + $.ajax({ + type: "POST", + url: "/workflow/create/", + data: { + "workflow_type": type + }, + headers: { + "X-CSRFToken": $('input[name="csrfmiddlewaretoken"]').val() + } + }).done(function (data, textStatus, jqXHR) { + window.location = "/workflow/"; + }).fail(function (jqxHR, textstatus) { + alert("Something went wrong..."); + }); +} + +function add_workflow(type) { + data = $.ajax({ + type: "POST", + url: "/workflow/add/", + data: { + "workflow_type": type + }, + headers: { + "X-CSRFToken": $('input[name="csrfmiddlewaretoken"]').val() + } + }).done(function (data, textStatus, jqXHR) { + update_page(data); + }).fail(function (jqxHR, textstatus) { + alert("Something went wrong..."); + }); +} + +function pop_workflow() { + data = $.ajax({ + type: "POST", + url: "/workflow/pop/", + headers: { + "X-CSRFToken": $('input[name="csrfmiddlewaretoken"]').val() + } + }).done(function (data, textStatus, jqXHR) { + update_page(data); + }).fail(function (jqxHR, textstatus) { + alert("Something went wrong..."); + }); +} + +function continue_workflow() { + window.location.replace("/workflow/"); +} + /////////////////// //Class Definitions /////////////////// diff --git a/src/templates/base.html b/src/templates/base.html index 891e0fc..f59740d 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -6,30 +6,7 @@ - + {% endblock %} {% block basecontent %} @@ -107,19 +84,19 @@ Express Booking - + Book a Pod - + Design a Pod - + Configure a Pod - + Create a Snapshot - + Configure OPNFV diff --git a/src/templates/dashboard/genericselect.html b/src/templates/dashboard/genericselect.html index f54cd90..863d33f 100644 --- a/src/templates/dashboard/genericselect.html +++ b/src/templates/dashboard/genericselect.html @@ -1,27 +1,22 @@ {% extends "workflow/viewport-element.html" %} -{% load staticfiles %} {% load bootstrap4 %} {% block content %} -
+

Create a Resource

Or select from the list below:

-
+ {% csrf_token %} {{ form|default:"

no form loaded

" }} - {% buttons %} - - {% endbuttons %}
@@ -33,13 +28,3 @@ {% endblock content %} -{% block onleave %} -var form = $("#{{select_type}}_select_form"); -var formData = form.serialize(); -var req = new XMLHttpRequest(); -req.open("POST", "/wf/workflow/", false); -req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); -req.onerror = function() { alert("problem with form submission"); } -req.send(formData); -{% endblock %} - diff --git a/src/templates/dashboard/landing.html b/src/templates/dashboard/landing.html index d4776cc..72f9e6e 100644 --- a/src/templates/dashboard/landing.html +++ b/src/templates/dashboard/landing.html @@ -47,13 +47,13 @@

- +
- +
- +
{% endif %} @@ -65,7 +65,7 @@

If you're a returning user, some of the following options may be of interest:

- -
{% csrf_token %} - - - -
-{% block vport_comm %} -{% endblock %} -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/dashboard/multiple_select_filter_widget.html b/src/templates/dashboard/multiple_select_filter_widget.html index 4a65bd9..e97a41b 100644 --- a/src/templates/dashboard/multiple_select_filter_widget.html +++ b/src/templates/dashboard/multiple_select_filter_widget.html @@ -1,6 +1,3 @@ - -
diff --git a/src/templates/dashboard/searchable_select_multiple.html b/src/templates/dashboard/searchable_select_multiple.html index 8299a55..44689da 100644 --- a/src/templates/dashboard/searchable_select_multiple.html +++ b/src/templates/dashboard/searchable_select_multiple.html @@ -1,5 +1,3 @@ - -
{% if incompatible == "true" %} -
- +
@@ -86,240 +54,24 @@
{% csrf_token %} - + + +
-
+ {% csrf_token %}
diff --git a/src/templates/workflow/viewport-element.html b/src/templates/workflow/viewport-element.html index bf13304..d16c924 100644 --- a/src/templates/workflow/viewport-element.html +++ b/src/templates/workflow/viewport-element.html @@ -5,42 +5,6 @@ {% block content %} {% endblock content %} - {% block vport_comm %} - - - {% endblock vport_comm %} - {% block validate_step %} - - - {% endblock validate_step %}
{% block element_messages %} diff --git a/src/workflow/models.py b/src/workflow/models.py index 866f442..0521165 100644 --- a/src/workflow/models.py +++ b/src/workflow/models.py @@ -318,6 +318,9 @@ class Confirmation_Step(WorkflowStep): default_flow_style=False ).strip() + if self.valid == WorkflowStepStatus.VALID: + context["confirm_succeeded"] = "true" + return context def flush_to_db(self): @@ -329,9 +332,7 @@ class Confirmation_Step(WorkflowStep): form = ConfirmationForm(post_data) if form.is_valid(): data = form.cleaned_data['confirm'] - context = self.get_context() if data == "True": - context["bypassed"] = "true" errors = self.flush_to_db() if errors: self.set_invalid("ERROR OCCURRED: " + errors) @@ -339,7 +340,6 @@ class Confirmation_Step(WorkflowStep): self.set_valid("Confirmed") elif data == "False": - context["bypassed"] = "true" self.set_valid("Canceled") else: self.set_invalid("Bad Form Contents") diff --git a/src/workflow/urls.py b/src/workflow/urls.py index ae620d0..298db95 100644 --- a/src/workflow/urls.py +++ b/src/workflow/urls.py @@ -11,7 +11,7 @@ from django.conf.urls import url from django.conf import settings -from workflow.views import delete_session, manager_view, viewport_view, add_workflow, cancel_workflow +from workflow.views import manager_view, viewport_view, add_workflow, remove_workflow, create_workflow from workflow.models import Repository from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info from workflow.booking_workflow import SWConfig_Select, Booking_Resource_Select, Booking_Meta @@ -19,10 +19,10 @@ from workflow.booking_workflow import SWConfig_Select, Booking_Resource_Select, app_name = 'workflow' urlpatterns = [ - url(r'^finish/$', delete_session, name='delete_session'), url(r'^manager/$', manager_view, name='manager'), url(r'^add/$', add_workflow, name='add_workflow'), - url(r'^cancel/$', cancel_workflow, name='cancel_workflow'), + url(r'^create/$', create_workflow, name='create_workflow'), + url(r'^pop/$', remove_workflow, name='remove_workflow'), url(r'^$', viewport_view, name='viewport') ] diff --git a/src/workflow/views.py b/src/workflow/views.py index 47241e2..3ab4d30 100644 --- a/src/workflow/views.py +++ b/src/workflow/views.py @@ -8,9 +8,8 @@ ############################################################################## -from django.http import HttpResponse, JsonResponse +from django.http import HttpResponse, JsonResponse, HttpResponseRedirect from django.shortcuts import render -from django.urls import reverse import uuid @@ -31,33 +30,17 @@ def attempt_auth(request): return None -def get_redirect_response(result): - if not result: - return {} - - # need to get type of result, and switch on the type - # since has_result, result must be populated with a valid object - if isinstance(result, Booking): - return { - 'redir_url': reverse('booking:booking_detail', kwargs={'booking_id': result.id}) - } - else: - return {} - - -def delete_session(request): +def remove_workflow(request): manager = attempt_auth(request) if not manager: return no_workflow(request) - not_last_workflow, result = manager.pop_workflow() + has_more_workflows, result = manager.pop_workflow() - if not_last_workflow: # this was not the last workflow, so don't redirect away - return JsonResponse({}) - else: + if not has_more_workflows: # this was the last workflow, so delete the reference to it in the tracker del ManagerTracker.managers[request.session['manager_session']] - return JsonResponse(get_redirect_response(result)) + return manager.render(request) def add_workflow(request): @@ -73,15 +56,6 @@ def add_workflow(request): return manager.render(request) # do we want this? -def cancel_workflow(request): - manager = attempt_auth(request) - if not manager: - return no_workflow(request) - - if not manager.pop_workflow(): - del ManagerTracker.managers[request.session['manager_session']] - - def manager_view(request): manager = attempt_auth(request) if not manager: @@ -98,16 +72,27 @@ def viewport_view(request): if manager is None: return no_workflow(request) - if request.method == 'GET': - return render(request, 'workflow/viewport-base.html') - else: - pass + if request.method != 'GET': + return HttpResponse(status=405) + return render(request, 'workflow/viewport-base.html') + + +def create_workflow(request): + if request.method != 'POST': + return HttpResponse(status=405) + workflow_type = request.POST.get('workflow_type') + try: + workflow_type = int(workflow_type) + except Exception: + return HttpResponse(status=400) + mgr_uuid = create_session(workflow_type, request=request,) + request.session['manager_session'] = mgr_uuid + return HttpResponse() def create_session(wf_type, request): - wf = int(wf_type) smgr = SessionManager(request=request) - smgr.add_workflow(workflow_type=wf, target_id=request.POST.get("target")) + smgr.add_workflow(workflow_type=wf_type, target_id=request.POST.get("target")) manager_uuid = uuid.uuid4().hex ManagerTracker.getInstance().managers[manager_uuid] = smgr diff --git a/src/workflow/workflow_manager.py b/src/workflow/workflow_manager.py index 605eee7..4677829 100644 --- a/src/workflow/workflow_manager.py +++ b/src/workflow/workflow_manager.py @@ -10,6 +10,7 @@ from django.http import JsonResponse from django.http.request import QueryDict +from django.urls import reverse from booking.models import Booking from workflow.workflow_factory import WorkflowFactory @@ -32,10 +33,9 @@ class SessionManager(): def __init__(self, request=None): self.workflows = [] - self.owner = request.user - self.factory = WorkflowFactory() + self.result = None def set_step_statuses(self, superclass_type, desired_enabled=True): workflow = self.active_workflow() @@ -62,6 +62,11 @@ class SessionManager(): ) ) + def get_redirect(self): + if isinstance(self.result, Booking): + return reverse('booking:booking_detail', kwargs={'booking_id': self.result.id}) + return "/" + def pop_workflow(self): multiple_wfs = len(self.workflows) > 1 if multiple_wfs: @@ -69,9 +74,13 @@ class SessionManager(): key = self.workflows[-1].repository.el[Repository.RESULT_KEY] result = self.workflows[-1].repository.el[Repository.RESULT] self.workflows[-2].repository.el[key] = result - self.workflows.pop() - current_repo = self.workflows[-1].repository - return (multiple_wfs, current_repo.el[current_repo.RESULT]) + prev_workflow = self.workflows.pop() + if self.workflows: + current_repo = self.workflows[-1].repository + else: + current_repo = prev_workflow.repository + self.result = current_repo.el[current_repo.RESULT] + return multiple_wfs, self.result def status(self, request): return { @@ -82,7 +91,7 @@ class SessionManager(): def handle_post(self, request): form = ManagerForm(request.POST) - if form.is_valid: + if form.is_valid(): self.get_active_step().post( QueryDict(form.cleaned_data['step_form']), user=request.user @@ -98,13 +107,18 @@ class SessionManager(): def handle_request(self, request): if request.method == 'POST': self.handle_post(request) - return self.render() + return self.render(request) def render(self, request, **kwargs): - return JsonResponse({ - "meta": self.status(), - "content": self.get_active_step().render_to_string(request) - }) + if self.workflows: + return JsonResponse({ + "meta": self.status(request), + "content": self.get_active_step().render_to_string(request), + }) + else: + return JsonResponse({ + "redirect": self.get_redirect() + }) def post_render(self, request): return self.active_workflow().steps[self.active_workflow().active_index].post_render(request) -- cgit 1.2.3-korg