<commit_before>/************ global variables ******/

/************ modal demo ************/
$modal_demo_msg_div='#createdemo_modal_errordiv';
$modal_demo_id='#createDemoFormModalId';
$modal_demo_header='div#createDemoModalheader';
$form_demo_id='#Demoform';
//hidden
$form_demo_field_demo_id="#Demoform #id_id";
//normal
$form_demo_field_editorsdemo_id="#Demoform #id_editorsdemoid";
$form_demo_field_title_id="#Demoform #id_title";
$form_demo_field_abstract_id="#Demoform #id_abstract";
$form_demo_field_zipURL_id="#Demoform #id_zipURL";
$form_demo_field_state="#Demoform #id_state";
var $demoform = $($form_demo_id);

/************ js error msgs *********/
$ws_down = "Please check if the webservices are running, in the status page.";

/************ Ace JSON Editor *******/
// Editor configuration
$editor = ace.edit("editor");
$editor.getSession().setMode("ace/mode/json");
$editor.getSession().setUseWrapMode(true);
$editor.getSession().setTabSize(4);
$editor.setAutoScrollEditorIntoView(true);

// Element to display messages about the DDL
$DDL_msg = document.getElementById('DDL_msg');
// Last version of DDL saved
last_DDL_saved = $editor.getValue();

// disable Save button, at the beginning
//disableSaveButton(true);

checkChanges();

// when changed the DDL in the editor, check if it is equal to the version saved
$editor.on("input", function() {
    checkChanges();
});

// check for unsaved changes in the DDL, and show a message in that case
function checkChanges(){
    // compare the last saved DDL with the current DDL in the editor
    if (last_DDL_saved.localeCompare($editor.getValue()) != 0){
        disableSaveButton(false);
        setDDLMessage('', 'There are unsaved changes.');
    }else{
        disableSaveButton(true);
        setDDLMessage('', 'DDL already saved.');
    }
}

// validate JSON DDL, and submit via AJAX if it is OK
function submitDDL(submit_URL){
    disableSaveButton(true);

    // >0 if errors found, ==0 if JSON is OK
    var json_is_valid = $editor.getSession().getAnnotations().length;
    var editor_value = $editor.getValue();

    // check syntax and it is not empty
    if (json_is_valid == 0 && editor_value != ''){
        // submit the DDL via AJAX
        setDDLMessage('', 'Saving...');
        submitDDLAJAX(submit_URL);
        disableSaveButton(false);
    }else{
        // show message to indicate errors
        setDDLMessage('KO', 'DDL not saved, please check the syntax.');
        disableSaveButton(false);
    }
}

// sets the given class and message, to the DDL Editor message
function setDDLMessage(msg_type, msg){
    if (msg_type == 'KO'){
        new_class = 'ststsnok';
    }else if(msg_type == 'OK'){
        new_class = 'ststsok';
    }else{
        new_class = 'ststsnormal';
    }

    // set the class to change color, etc
    $DDL_msg.className = new_class;
    $DDL_msg.style.display = 'initial';
    // set the message to the element
    $DDL_msg.innerHTML = msg;
}

// disables the Save DDL button when it is
// already saved, or while is being saved
function disableSaveButton(arg){
    // button to save the DDL
    $save_btn = document.getElementById('save-DDL-btn');

    if (arg == true){
        $save_btn.className += " btn-disabled";
    }else{
        $save_btn.classList.remove("btn-disabled");
    }
}

//  find errors if WS response is KO
function find_ws_errors(data) {
    var error_msg = null;

    if (data.code){
        error_msg = "code: " + data.code;
    }
    if (data.error){
        error_msg = "error: " + data.error;
    }
    console.log("error_msg: " + error_msg)
    return error_msg
}

/************ CSRF check ************/

// secure AJAX POST to ws ,from django docs
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$(document).ready(function(){
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });

    // use JQuery to refresh the editor when its container div is resized
    $( "#editor" ).resizable({
        resize: function( event, ui ) {
            $editor.resize();
        }
    });
});

/************ DDL Form ************/

// submit DDL form
function submitDDLAJAX(submit_URL){
    var csrftoken = getCookie('csrftoken');

    // Demo ID value
    var demoid_value = document.getElementById('demoid').value;
    // DDL value
    var DDL_value = $editor.getValue();
    // serialize data before send
    var $serializedData = $.param({demoid: demoid_value, ddlJSON: DDL_value });
    // URL to submit the data
    var $ajaxurl = submit_URL;

    $.ajax({
        url: $ajaxurl,
        type: "POST",
        // dataType: "json",
        data: $serializedData,
        success: function(data) {
            console.log("AJAX CALL status: "+data.status);
            if (data.status == "OK") {
                setDDLMessage('OK', 'DDL succesfully saved.');
                last_DDL_saved = DDL_value;
                disableSaveButton(true);
            }else {
                console.log("status KO");
                setDDLMessage('KO', 'Could not save the DDL. Status: \'' + data.status + '\'.');
            }
        },
        error: function () {
            console.log("ajax error");
            setDDLMessage('KO', 'Error saving the DDL: ' + $ws_down);
        }
    });
}

/************ Demo Form ************/

//  delete demo
//  todo: change browser dialogs for confirm, use http://jqueryui.com/dialog/
/*
parameters:
    wsurl: address of the service to call
    demo_editorsdemoid: ID of the demo to delete
    demo_title: title of the demo to delete
    redirect_url: URL to redirect after deletion of the demo
*/
function send_delete_demo_request(wsurl, demo_editorsdemoid, demo_title, redirect_url) {

    var deldemo = confirm("WARNING: this operation will remove completely demo with editor's id: "+
            demo_editorsdemoid + " ("+ demo_title +") and all its associated data from the system. " +
            "\n\nNote that you can, instead, change the state of the demo to testing, which is 'safe'.");

    if (deldemo == true) {
        var deldemo2 = confirm( "Are you sure to delete completely this demo? " +
            "\nPlease confirm if you want to remove it from the system");

        if (deldemo2 == true) {
            $.ajax({
                type: 'POST',
                url: wsurl,
                dataType: 'json',
                success: function(data) {
                    console.log(data.status);
                    alert('Demo ' + demo_editorsdemoid + ' deleted succesfully');
                    // after the deletion, redirect to given URL (list of demos)
                    window.location.replace(redirect_url);
                },
                error: function(data){
                    console.error(data.status);
                    alert('Demo not deleted: ' + $ws_down);
                }
            });
        }
    }
}

// show/edit Demo modal
function send_get_demo_request(wsurl,editor_demo_id){
    console.log(wsurl);
    console.log(editor_demo_id);

    if (editor_demo_id === undefined) {
        console.log("send_get_demo_request  editor_demo_id === undefined");
        // editor_demo_id was not passed, just open create modal
        $($modal_demo_header).html('
New Demo data
');
        //clear data
        $($modal_demo_msg_div).html('');
        $($form_demo_field_demo_id).get(0).value = '';
        $($form_demo_field_editorsdemo_id).get(0).value = '';
        $($form_demo_field_title_id).get(0).value = '';
        $($form_demo_field_abstract_id).get(0).value = '';
        $($form_demo_field_zipURL_id).get(0).value = '';
        $($form_demo_field_state).get(0).value = '';
        $demoform.show();
        $($modal_demo_id).modal('show');

    }else{
        // wsurl was passed, get data from ws and open show modal
        console.log("send_get_demo_request");
        $.ajax({
            type: 'POST',
            url: wsurl,
            dataType: 'json',
            success: function(data) {
                console.log(data.status);
                console.log(data);

                if (data.status=='OK'){
                    //clear modal  error
                    $($modal_demo_msg_div).html('');

                    //edit/show demo
                    if ( data.editorsdemoid ){
                        //Load ddl data in form
                        //clear error data
                        $($modal_demo_msg_div).html('');
                        $($modal_demo_header).html('
Edit Demo data
');
                        //  $($form_ddl_field_demo_id).get(0).value = demo_id;
                        //  $($form_ddl_field_ddl_id).get(0).value = data.last_demodescription.id;
                        //  $($form_ddl_field_ddljson).get(0).value = $.parseJSON(data.last_demodescription.json);
                        $($form_demo_field_demo_id).get(0).value = data.editorsdemoid;
                        $($form_demo_field_editorsdemo_id).get(0).value = data.editorsdemoid;
                        $($form_demo_field_title_id).get(0).value = data.title;
                        $($form_demo_field_abstract_id).get(0).value = data.abstract;
                        $($form_demo_field_zipURL_id).get(0).value = data.zipURL;
                        $($form_demo_field_state).get(0).value = data.state;
                        $($modal_demo_id).modal('show');
                        $demoform.show();

                    //create demo
                    }else{
                        console.log("error no demoid in form ");
                        //error, no OK
                        var errorhtml="
Demo Data not retrieved sucessfully, check demoinfo consistency
";
                        $($modal_demo_msg_div).html(errorhtml);
                        //clear form data
                        $($form_demo_field_demo_id).get(0).value = '';
                        $($form_demo_field_editorsdemo_id).get(0).value = '';
                        $($form_demo_field_title_id).get(0).value = '';
                        $($form_demo_field_abstract_id).get(0).value = '';
                        $($form_demo_field_zipURL_id).get(0).value = '';
                        $($form_demo_field_state).get(0).value = '';
                        $($modal_demo_id).modal('show');
                        //$demoform.show();
                    }
                }else{
                    //error, not OK
                    console.log(" Error, ws returned KO");

                    $error_msg=find_ws_errors(data);
                    var errorhtml="
Demo not retrieved sucessfully, ws returned "+$error_msg+"
";
                    $($modal_demo_id).modal('show');
                    $demoform.hide();
                    $($modal_demo_msg_div).html(errorhtml);
                }
            },
            error: function(data){
                var errorhtml="
Error getting Demo: "+$ws_down+"
";
                $($modal_demo_msg_div).html(errorhtml);
                $($modal_demo_id).modal('show');
                $demoform.hide();
            }
        });
    }
}

// submit Demo form
function submitDemoformAJAX(){

    console.log("submitDemoformAJAX Submit");

    var csrftoken = getCookie('csrftoken');
    var $serializedData = $demoform.serialize();
    var $ajaxurl = $demoform.attr("action");

    console.log($ajaxurl);

    $.ajax({
        url: $ajaxurl,
        type: "POST",
        // dataType: "json",
        data: $serializedData,
        success: function(data) {

            console.log("AJAX CALL status: "+data.status);
            console.log("AJAX CALL error: "+data.error);
            console.log("AJAX CALL data: "+data);

            if (data.status == "OK") {
                $($modal_demo_msg_div).html('Demo saved').show();
                $demoform.hide();
                //todo better to only reload part of list, but should change django pagination for js pagination
                window.location.reload(true);
            }
            else {
                $demoform.show();
                console.log(" Error, ws returned KO");
                $error_msg = find_ws_errors(data);
                var errorhtml="
Demo not saved, ws returned  "+$error_msg+"
";
                $($modal_demo_msg_div).html(errorhtml).show();
            }
        },
        error: function () {
            console.log("ajax error");
            var errorhtml="
Error saving Demo: "+$ws_down+"
";
            $($modal_demo_msg_div).html(errorhtml);
            $($modal_demo_id).modal('show');
            $demoform.hide();
        }
    });
}

//  Demo form validation
$($form_demo_id).validate({
    errorClass: "validationerror",//for css
    rules: {
        title: {
            required: true,
            minlength: 5
        },
        abstract: {
            required: true,
            minlength: 5
        },
        state: {
            required: true
        },
        editorsdemoid: {
            required: true
        },
        zipURL: {
            required: true,
            minlength: 5
        }
    },
    messages: {

        title: {
            required: "please fill title field",
            minlength: "Your title must be at least 5 characters"
        },
        abstract: {
            required: "please fill abstract field",
            minlength: "Your abstract must be at least 5 characters"
        },
        state: {
            required: "please fill  state"
        },
        editorsdemoid: {
            required: "please fill  editorsdemoid"
        },
        zipURL: {
            required: "please fill zipURL field",
            minlength: "Your zipURL must be at least 5 characters"
        }
    },
    submitHandler: function() {
        console.log("validated ok submitDemoformAJAX");
        // your ajax loading logic
        // form.submit(); // use this to finally submit form data at the last step
        submitDemoformAJAX();
        return false;  // prevent form submit because you are doing the ajax
    }
});
<commit_msg>[CP] Redirect to new URL when demo ID is changed.<commit_after>/************ global variables ******/

/************ modal demo ************/
$modal_demo_msg_div='#createdemo_modal_errordiv';
$modal_demo_id='#createDemoFormModalId';
$modal_demo_header='div#createDemoModalheader';
$form_demo_id='#Demoform';
//hidden
$form_demo_field_demo_id="#Demoform #id_id";
//normal
$form_demo_field_editorsdemo_id="#Demoform #id_editorsdemoid";
$form_demo_field_title_id="#Demoform #id_title";
$form_demo_field_abstract_id="#Demoform #id_abstract";
$form_demo_field_zipURL_id="#Demoform #id_zipURL";
$form_demo_field_state="#Demoform #id_state";
var $demoform = $($form_demo_id);

/************ js error msgs *********/
$ws_down = "Please check if the webservices are running, in the status page.";

/************ Ace JSON Editor *******/
// Editor configuration
$editor = ace.edit("editor");
$editor.getSession().setMode("ace/mode/json");
$editor.getSession().setUseWrapMode(true);
$editor.getSession().setTabSize(4);
$editor.setAutoScrollEditorIntoView(true);

// Element to display messages about the DDL
$DDL_msg = document.getElementById('DDL_msg');
// Last version of DDL saved
last_DDL_saved = $editor.getValue();

// disable Save button, at the beginning
//disableSaveButton(true);

checkChanges();

// when changed the DDL in the editor, check if it is equal to the version saved
$editor.on("input", function() {
    checkChanges();
});

// check for unsaved changes in the DDL, and show a message in that case
function checkChanges(){
    // compare the last saved DDL with the current DDL in the editor
    if (last_DDL_saved.localeCompare($editor.getValue()) != 0){
        disableSaveButton(false);
        setDDLMessage('', 'There are unsaved changes.');
    }else{
        disableSaveButton(true);
        setDDLMessage('', 'DDL already saved.');
    }
}

// validate JSON DDL, and submit via AJAX if it is OK
function submitDDL(submit_URL){
    disableSaveButton(true);

    // >0 if errors found, ==0 if JSON is OK
    var json_is_valid = $editor.getSession().getAnnotations().length;
    var editor_value = $editor.getValue();

    // check syntax and it is not empty
    if (json_is_valid == 0 && editor_value != ''){
        // submit the DDL via AJAX
        setDDLMessage('', 'Saving...');
        submitDDLAJAX(submit_URL);
        disableSaveButton(false);
    }else{
        // show message to indicate errors
        setDDLMessage('KO', 'DDL not saved, please check the syntax.');
        disableSaveButton(false);
    }
}

// sets the given class and message, to the DDL Editor message
function setDDLMessage(msg_type, msg){
    if (msg_type == 'KO'){
        new_class = 'ststsnok';
    }else if(msg_type == 'OK'){
        new_class = 'ststsok';
    }else{
        new_class = 'ststsnormal';
    }

    // set the class to change color, etc
    $DDL_msg.className = new_class;
    $DDL_msg.style.display = 'initial';
    // set the message to the element
    $DDL_msg.innerHTML = msg;
}

// disables the Save DDL button when it is
// already saved, or while is being saved
function disableSaveButton(arg){
    // button to save the DDL
    $save_btn = document.getElementById('save-DDL-btn');

    if (arg == true){
        $save_btn.className += " btn-disabled";
    }else{
        $save_btn.classList.remove("btn-disabled");
    }
}

//  find errors if WS response is KO
function find_ws_errors(data) {
    var error_msg = null;

    if (data.code){
        error_msg = "code: " + data.code;
    }
    if (data.error){
        error_msg = "error: " + data.error;
    }
    console.log("error_msg: " + error_msg)
    return error_msg
}

/************ CSRF check ************/

// secure AJAX POST to ws ,from django docs
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$(document).ready(function(){
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });

    // use JQuery to refresh the editor when its container div is resized
    $( "#editor" ).resizable({
        resize: function( event, ui ) {
            $editor.resize();
        }
    });
});

/************ DDL Form ************/

// submit DDL form
function submitDDLAJAX(submit_URL){
    var csrftoken = getCookie('csrftoken');

    // Demo ID value
    var demoid_value = document.getElementById('demoid').value;
    // DDL value
    var DDL_value = $editor.getValue();
    // serialize data before send
    var $serializedData = $.param({demoid: demoid_value, ddlJSON: DDL_value });
    // URL to submit the data
    var $ajaxurl = submit_URL;

    $.ajax({
        url: $ajaxurl,
        type: "POST",
        // dataType: "json",
        data: $serializedData,
        success: function(data) {
            console.log("AJAX CALL status: "+data.status);
            if (data.status == "OK") {
                setDDLMessage('OK', 'DDL succesfully saved.');
                last_DDL_saved = DDL_value;
                disableSaveButton(true);
            }else {
                console.log("status KO");
                setDDLMessage('KO', 'Could not save the DDL. Status: \'' + data.status + '\'.');
            }
        },
        error: function () {
            console.log("ajax error");
            setDDLMessage('KO', 'Error saving the DDL: ' + $ws_down);
        }
    });
}

/************ Demo Form ************/

//  delete demo
//  todo: change browser dialogs for confirm, use http://jqueryui.com/dialog/
/*
parameters:
    wsurl: address of the service to call
    demo_editorsdemoid: ID of the demo to delete
    demo_title: title of the demo to delete
    redirect_url: URL to redirect after deletion of the demo
*/
function send_delete_demo_request(wsurl, demo_editorsdemoid, demo_title, redirect_url) {

    var deldemo = confirm("WARNING: this operation will remove completely demo with editor's id: "+
            demo_editorsdemoid + " ("+ demo_title +") and all its associated data from the system. " +
            "\n\nNote that you can, instead, change the state of the demo to testing, which is 'safe'.");

    if (deldemo == true) {
        var deldemo2 = confirm( "Are you sure to delete completely this demo? " +
            "\nPlease confirm if you want to remove it from the system");

        if (deldemo2 == true) {
            $.ajax({
                type: 'POST',
                url: wsurl,
                dataType: 'json',
                success: function(data) {
                    console.log(data.status);
                    alert('Demo ' + demo_editorsdemoid + ' deleted succesfully');
                    // after the deletion, redirect to given URL (list of demos)
                    window.location.replace(redirect_url);
                },
                error: function(data){
                    console.error(data.status);
                    alert('Demo not deleted: ' + $ws_down);
                }
            });
        }
    }
}

// show/edit Demo modal
function send_get_demo_request(wsurl,editor_demo_id){
    console.log(wsurl);
    console.log(editor_demo_id);

    if (editor_demo_id === undefined) {
        console.log("send_get_demo_request  editor_demo_id === undefined");
        // editor_demo_id was not passed, just open create modal
        $($modal_demo_header).html('
New Demo data
');
        //clear data
        $($modal_demo_msg_div).html('');
        $($form_demo_field_demo_id).get(0).value = '';
        $($form_demo_field_editorsdemo_id).get(0).value = '';
        $($form_demo_field_title_id).get(0).value = '';
        $($form_demo_field_abstract_id).get(0).value = '';
        $($form_demo_field_zipURL_id).get(0).value = '';
        $($form_demo_field_state).get(0).value = '';
        $demoform.show();
        $($modal_demo_id).modal('show');

    }else{
        // wsurl was passed, get data from ws and open show modal
        console.log("send_get_demo_request");
        $.ajax({
            type: 'POST',
            url: wsurl,
            dataType: 'json',
            success: function(data) {
                console.log(data.status);
                console.log(data);

                if (data.status=='OK'){
                    //clear modal  error
                    $($modal_demo_msg_div).html('');

                    //edit/show demo
                    if ( data.editorsdemoid ){
                        //Load ddl data in form
                        //clear error data
                        $($modal_demo_msg_div).html('');
                        $($modal_demo_header).html('
Edit Demo data
');
                        //  $($form_ddl_field_demo_id).get(0).value = demo_id;
                        //  $($form_ddl_field_ddl_id).get(0).value = data.last_demodescription.id;
                        //  $($form_ddl_field_ddljson).get(0).value = $.parseJSON(data.last_demodescription.json);
                        $($form_demo_field_demo_id).get(0).value = data.editorsdemoid;
                        $($form_demo_field_editorsdemo_id).get(0).value = data.editorsdemoid;
                        $($form_demo_field_title_id).get(0).value = data.title;
                        $($form_demo_field_abstract_id).get(0).value = data.abstract;
                        $($form_demo_field_zipURL_id).get(0).value = data.zipURL;
                        $($form_demo_field_state).get(0).value = data.state;
                        $($modal_demo_id).modal('show');
                        $demoform.show();

                    //create demo
                    }else{
                        console.log("error no demoid in form ");
                        //error, no OK
                        var errorhtml="
Demo Data not retrieved sucessfully, check demoinfo consistency
";
                        $($modal_demo_msg_div).html(errorhtml);
                        //clear form data
                        $($form_demo_field_demo_id).get(0).value = '';
                        $($form_demo_field_editorsdemo_id).get(0).value = '';
                        $($form_demo_field_title_id).get(0).value = '';
                        $($form_demo_field_abstract_id).get(0).value = '';
                        $($form_demo_field_zipURL_id).get(0).value = '';
                        $($form_demo_field_state).get(0).value = '';
                        $($modal_demo_id).modal('show');
                        //$demoform.show();
                    }
                }else{
                    //error, not OK
                    console.log(" Error, ws returned KO");

                    $error_msg=find_ws_errors(data);
                    var errorhtml="
Demo not retrieved sucessfully, ws returned "+$error_msg+"
";
                    $($modal_demo_id).modal('show');
                    $demoform.hide();
                    $($modal_demo_msg_div).html(errorhtml);
                }
            },
            error: function(data){
                var errorhtml="
Error getting Demo: "+$ws_down+"
";
                $($modal_demo_msg_div).html(errorhtml);
                $($modal_demo_id).modal('show');
                $demoform.hide();
            }
        });
    }
}

// submit Demo form
function submitDemoformAJAX(){

    console.log("submitDemoformAJAX Submit");

    var csrftoken = getCookie('csrftoken');
    var $serializedData = $demoform.serialize();
    var $ajaxurl = $demoform.attr("action");

    console.log($ajaxurl);

    $.ajax({
        url: $ajaxurl,
        type: "POST",
        // dataType: "json",
        data: $serializedData,
        success: function(data) {

            console.log("AJAX CALL status: "+data.status);
            console.log("AJAX CALL error: "+data.error);
            console.log("AJAX CALL data: "+data);

            if (data.status == "OK") {
                $($modal_demo_msg_div).html('Demo saved').show();
                $new_demo_id = document.getElementById('id_editorsdemoid').value;
                $old_demo_id = document.getElementById('id_id').value;
                $demoform.hide();

                // check if demo id has changed, and redirect to the new URL, if it is the case
                if($new_demo_id == $old_demo_id){
                    window.location.reload(true);
                }else{
                    $current_demo_URL = window.location.href;
                    $new_demo_URL = $current_demo_URL.replace($old_demo_id, $new_demo_id);
                    window.location.replace($new_demo_URL);
                }
            }
            else {
                $demoform.show();
                console.log(" Error, ws returned KO");
                $error_msg = find_ws_errors(data);
                var errorhtml="
Demo not saved, ws returned  "+$error_msg+"
";
                $($modal_demo_msg_div).html(errorhtml).show();
            }
        },
        error: function () {
            console.log("ajax error");
            var errorhtml="
Error saving Demo: "+$ws_down+"
";
            $($modal_demo_msg_div).html(errorhtml);
            $($modal_demo_id).modal('show');
            $demoform.hide();
        }
    });
}

//  Demo form validation
$($form_demo_id).validate({
    errorClass: "validationerror",//for css
    rules: {
        title: {
            required: true,
            minlength: 5
        },
        abstract: {
            required: true,
            minlength: 5
        },
        state: {
            required: true
        },
        editorsdemoid: {
            required: true
        },
        zipURL: {
            required: true,
            minlength: 5
        }
    },
    messages: {

        title: {
            required: "please fill title field",
            minlength: "Your title must be at least 5 characters"
        },
        abstract: {
            required: "please fill abstract field",
            minlength: "Your abstract must be at least 5 characters"
        },
        state: {
            required: "please fill  state"
        },
        editorsdemoid: {
            required: "please fill  editorsdemoid"
        },
        zipURL: {
            required: "please fill zipURL field",
            minlength: "Your zipURL must be at least 5 characters"
        }
    },
    submitHandler: function() {
        console.log("validated ok submitDemoformAJAX");
        // your ajax loading logic
        // form.submit(); // use this to finally submit form data at the last step
        submitDemoformAJAX();
        return false;  // prevent form submit because you are doing the ajax
    }
});
<|endoftext|>


<commit_before>/**
 * Backbone localStorage Adapter
 * Version 1.1.9
 *
 * https://github.com/jeromegn/Backbone.localStorage
 */
(function (root, factory) {
  if (typeof exports === 'object' && typeof require === 'function') {
    module.exports = factory(require("backbone"));
  } else if (typeof define === "function" && define.amd) {
    // AMD. Register as an anonymous module.
    define(["backbone"], function(Backbone) {
      // Use global variables if the locals are undefined.
      return factory(Backbone || root.Backbone);
    });
  } else {
    factory(Backbone);
  }
}(this, function(Backbone) {
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.

// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace

// Generate four random hex digits.
function S4() {
   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};

// Generate a pseudo-GUID by concatenating random hexadecimal.
function guid() {
   return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
};

function contains(array, item) {
  var i = array.length;
  while (i--) if (array[i] === item) return true;
  return false;
}

function extend(obj, props) {
  for (var key in props) obj[key] = props[key]
  return obj;
}

// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function(name, serializer) {
  if( !this.localStorage ) {
    throw "Backbone.localStorage: Environment does not support localStorage."
  }
  this.name = name;
  this.serializer = serializer || {
    serialize: function(item) {
      return _.isObject(item) ? JSON.stringify(item) : item;
    },
    // fix for "illegal access" error on Android when JSON.parse is passed null
    deserialize: function (data) {
      return data && JSON.parse(data);
    }
  };
  var store = this.localStorage().getItem(this.name);
  this.records = (store && store.split(",")) || [];
};

extend(Backbone.LocalStorage.prototype, {

  // Save the current state of the **Store** to *localStorage*.
  save: function() {
    this.localStorage().setItem(this.name, this.records.join(","));
  },

  // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
  // have an id of it's own.
  create: function(model) {
    if (!model.id) {
      model.id = guid();
      model.set(model.idAttribute, model.id);
    }
    this.localStorage().setItem(this._itemName(model.id), this.serializer.serialize(model));
    this.records.push(model.id.toString());
    this.save();
    return this.find(model) !== false;
  },

  // Update a model by replacing its copy in `this.data`.
  update: function(model) {
    this.localStorage().setItem(this._itemName(model.id), this.serializer.serialize(model));
    var modelId = model.id.toString();
    if (!contains(this.records, modelId)) {
      this.records.push(modelId);
      this.save();
    }
    return this.find(model) !== false;
  },

  // Retrieve a model from `this.data` by id.
  find: function(model) {
    return this.serializer.deserialize(this.localStorage().getItem(this._itemName(model.id)));
  },

  // Return the array of all models currently in storage.
  findAll: function() {
    var result = [];
    for (var i = 0, id, data; i < this.records.length; i++) {
      id = this.records[i];
      data = this.serializer.deserialize(this.localStorage().getItem(this._itemName(id)));
      if (data != null) result.push(data);
    }
    return result;
  },

  // Delete a model from `this.data`, returning it.
  destroy: function(model) {
    this.localStorage().removeItem(this._itemName(model.id));
    var modelId = model.id.toString();
    for (var i = 0, id; i < this.records.length; i++) {
      if (this.records[i] === modelId) {
        this.records.splice(i, 1);
      }
    }
    this.save();
    return model;
  },

  localStorage: function() {
    return localStorage;
  },

  // Clear localStorage for specific collection.
  _clear: function() {
    var local = this.localStorage(),
      itemRe = new RegExp("^" + this.name + "-");

    // Remove id-tracking item (e.g., "foo").
    local.removeItem(this.name);

    // Match all data items (e.g., "foo-ID") and remove.
    for (var k in local) {
      if (itemRe.test(k)) {
        local.removeItem(k);
      }
    }

    this.records.length = 0;
  },

  // Size of localStorage.
  _storageSize: function() {
    return this.localStorage().length;
  },

  _itemName: function(id) {
    return this.name+"-"+id;
  }

});

// localSync delegate to the model or collection's
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
  var store = model.localStorage || model.collection.localStorage;

  var resp, errorMessage;
  //If $ is having Deferred - use it.
  var syncDfd = Backbone.$ ?
    (Backbone.$.Deferred && Backbone.$.Deferred()) :
    (Backbone.Deferred && Backbone.Deferred());

  try {

    switch (method) {
      case "read":
        resp = model.id != undefined ? store.find(model) : store.findAll();
        break;
      case "create":
        resp = store.create(model);
        break;
      case "update":
        resp = store.update(model);
        break;
      case "delete":
        resp = store.destroy(model);
        break;
    }

  } catch(error) {
    if (error.code === 22 && store._storageSize() === 0)
      errorMessage = "Private browsing is unsupported";
    else
      errorMessage = error.message;
  }

  if (resp) {
    if (options && options.success) {
      if (Backbone.VERSION === "0.9.10") {
        options.success(model, resp, options);
      } else {
        options.success(resp);
      }
    }
    if (syncDfd) {
      syncDfd.resolve(resp);
    }

  } else {
    errorMessage = errorMessage ? errorMessage
                                : "Record Not Found";

    if (options && options.error)
      if (Backbone.VERSION === "0.9.10") {
        options.error(model, errorMessage, options);
      } else {
        options.error(errorMessage);
      }

    if (syncDfd)
      syncDfd.reject(errorMessage);
  }

  // add compatibility with $.ajax
  // always execute callback for success and error
  if (options && options.complete) options.complete(resp);

  return syncDfd && syncDfd.promise();
};

Backbone.ajaxSync = Backbone.sync;

Backbone.getSyncMethod = function(model) {
  if(model.localStorage || (model.collection && model.collection.localStorage)) {
    return Backbone.localSync;
  }

  return Backbone.ajaxSync;
};

// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function(method, model, options) {
  return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
};

return Backbone.LocalStorage;
}));
<commit_msg>add underscore to factory as there it is depended<commit_after>/**
 * Backbone localStorage Adapter
 * Version 1.1.9
 *
 * https://github.com/jeromegn/Backbone.localStorage
 */
(function (root, factory) {
  if (typeof exports === 'object' && typeof require === 'function') {
    module.exports = factory(require("backbone"), require('underscore'));
  } else if (typeof define === "function" && define.amd) {
    // AMD. Register as an anonymous module.
    define(["backbone", "underscore"], function(Backbone, _) {
      // Use global variables if the locals are undefined.
      return factory(Backbone || root.Backbone, _ || root._);
    });
  } else {
    factory(Backbone, _);
  }
}(this, function(Backbone, _) {
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.

// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace

// Generate four random hex digits.
function S4() {
   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};

// Generate a pseudo-GUID by concatenating random hexadecimal.
function guid() {
   return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
};

function contains(array, item) {
  var i = array.length;
  while (i--) if (array[i] === item) return true;
  return false;
}

function extend(obj, props) {
  for (var key in props) obj[key] = props[key]
  return obj;
}

// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function(name, serializer) {
  if( !this.localStorage ) {
    throw "Backbone.localStorage: Environment does not support localStorage."
  }
  this.name = name;
  this.serializer = serializer || {
    serialize: function(item) {
      return _.isObject(item) ? JSON.stringify(item) : item;
    },
    // fix for "illegal access" error on Android when JSON.parse is passed null
    deserialize: function (data) {
      return data && JSON.parse(data);
    }
  };
  var store = this.localStorage().getItem(this.name);
  this.records = (store && store.split(",")) || [];
};

extend(Backbone.LocalStorage.prototype, {

  // Save the current state of the **Store** to *localStorage*.
  save: function() {
    this.localStorage().setItem(this.name, this.records.join(","));
  },

  // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
  // have an id of it's own.
  create: function(model) {
    if (!model.id) {
      model.id = guid();
      model.set(model.idAttribute, model.id);
    }
    this.localStorage().setItem(this._itemName(model.id), this.serializer.serialize(model));
    this.records.push(model.id.toString());
    this.save();
    return this.find(model) !== false;
  },

  // Update a model by replacing its copy in `this.data`.
  update: function(model) {
    this.localStorage().setItem(this._itemName(model.id), this.serializer.serialize(model));
    var modelId = model.id.toString();
    if (!contains(this.records, modelId)) {
      this.records.push(modelId);
      this.save();
    }
    return this.find(model) !== false;
  },

  // Retrieve a model from `this.data` by id.
  find: function(model) {
    return this.serializer.deserialize(this.localStorage().getItem(this._itemName(model.id)));
  },

  // Return the array of all models currently in storage.
  findAll: function() {
    var result = [];
    for (var i = 0, id, data; i < this.records.length; i++) {
      id = this.records[i];
      data = this.serializer.deserialize(this.localStorage().getItem(this._itemName(id)));
      if (data != null) result.push(data);
    }
    return result;
  },

  // Delete a model from `this.data`, returning it.
  destroy: function(model) {
    this.localStorage().removeItem(this._itemName(model.id));
    var modelId = model.id.toString();
    for (var i = 0, id; i < this.records.length; i++) {
      if (this.records[i] === modelId) {
        this.records.splice(i, 1);
      }
    }
    this.save();
    return model;
  },

  localStorage: function() {
    return localStorage;
  },

  // Clear localStorage for specific collection.
  _clear: function() {
    var local = this.localStorage(),
      itemRe = new RegExp("^" + this.name + "-");

    // Remove id-tracking item (e.g., "foo").
    local.removeItem(this.name);

    // Match all data items (e.g., "foo-ID") and remove.
    for (var k in local) {
      if (itemRe.test(k)) {
        local.removeItem(k);
      }
    }

    this.records.length = 0;
  },

  // Size of localStorage.
  _storageSize: function() {
    return this.localStorage().length;
  },

  _itemName: function(id) {
    return this.name+"-"+id;
  }

});

// localSync delegate to the model or collection's
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
  var store = model.localStorage || model.collection.localStorage;

  var resp, errorMessage;
  //If $ is having Deferred - use it.
  var syncDfd = Backbone.$ ?
    (Backbone.$.Deferred && Backbone.$.Deferred()) :
    (Backbone.Deferred && Backbone.Deferred());

  try {

    switch (method) {
      case "read":
        resp = model.id != undefined ? store.find(model) : store.findAll();
        break;
      case "create":
        resp = store.create(model);
        break;
      case "update":
        resp = store.update(model);
        break;
      case "delete":
        resp = store.destroy(model);
        break;
    }

  } catch(error) {
    if (error.code === 22 && store._storageSize() === 0)
      errorMessage = "Private browsing is unsupported";
    else
      errorMessage = error.message;
  }

  if (resp) {
    if (options && options.success) {
      if (Backbone.VERSION === "0.9.10") {
        options.success(model, resp, options);
      } else {
        options.success(resp);
      }
    }
    if (syncDfd) {
      syncDfd.resolve(resp);
    }

  } else {
    errorMessage = errorMessage ? errorMessage
                                : "Record Not Found";

    if (options && options.error)
      if (Backbone.VERSION === "0.9.10") {
        options.error(model, errorMessage, options);
      } else {
        options.error(errorMessage);
      }

    if (syncDfd)
      syncDfd.reject(errorMessage);
  }

  // add compatibility with $.ajax
  // always execute callback for success and error
  if (options && options.complete) options.complete(resp);

  return syncDfd && syncDfd.promise();
};

Backbone.ajaxSync = Backbone.sync;

Backbone.getSyncMethod = function(model) {
  if(model.localStorage || (model.collection && model.collection.localStorage)) {
    return Backbone.localSync;
  }

  return Backbone.ajaxSync;
};

// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function(method, model, options) {
  return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
};

return Backbone.LocalStorage;
}));
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import store from '@/store'

export default {
  name: 'host',
  title: 'label.hosts',
  icon: 'desktop',
  permission: ['listHostsMetrics'],
  resourceType: 'Host',
  params: { type: 'routing' },
  columns: () => {
    const fields = ['name', 'state', 'resourcestate', 'ipaddress', 'hypervisor', 'instances', 'powerstate']
    const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite']
    if (store.getters.metrics) {
      fields.push(...metricsFields)
    }
    fields.push('clustername')
    fields.push('zonename')
    return fields
  },
  details: ['name', 'id', 'resourcestate', 'ipaddress', 'hypervisor', 'type', 'clustername', 'podname', 'zonename', 'disconnected', 'created'],
  tabs: [{
    name: 'details',
    component: () => import('@/components/view/DetailsTab.vue')
  }],
  related: [{
    name: 'vm',
    title: 'label.instances',
    param: 'hostid'
  }],
  actions: [
    {
      api: 'addHost',
      icon: 'plus',
      label: 'label.add.host',
      docHelp: 'adminguide/installguide/configuration.html#adding-a-host',
      listView: true,
      popup: true,
      component: () => import('@/views/infra/HostAdd.vue')
    },
    {
      api: 'updateHost',
      icon: 'edit',
      label: 'label.edit',
      dataView: true,
      args: ['name', 'hosttags', 'oscategoryid'],
      mapping: {
        oscategoryid: {
          api: 'listOsCategories'
        }
      }
    },
    {
      api: 'provisionCertificate',
      icon: 'safety-certificate',
      label: 'label.action.secure.host',
      message: 'message.action.secure.host',
      dataView: true,
      show: (record) => { return record.hypervisor === 'KVM' },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'reconnectHost',
      icon: 'forward',
      label: 'label.action.force.reconnect',
      message: 'message.confirm.action.force.reconnect',
      dataView: true,
      show: (record) => { return ['Disconnected', 'Up'].includes(record.state) }
    },
    {
      api: 'updateHost',
      icon: 'pause-circle',
      label: 'label.disable.host',
      message: 'message.confirm.disable.host',
      dataView: true,
      defaultArgs: { allocationstate: 'Disable' },
      show: (record) => { return record.resourcestate === 'Enabled' }
    },
    {
      api: 'updateHost',
      icon: 'play-circle',
      label: 'label.enable.host',
      message: 'message.confirm.enable.host',
      dataView: true,
      defaultArgs: { allocationstate: 'Enable' },
      show: (record) => { return record.resourcestate === 'Disabled' }
    },
    {
      api: 'prepareHostForMaintenance',
      icon: 'plus-square',
      label: 'label.action.enable.maintenance.mode',
      message: 'message.action.host.enable.maintenance.mode',
      docHelp: 'adminguide/hosts.html#maintaining-hypervisors-on-hosts',
      dataView: true,
      show: (record) => { return record.resourcestate === 'Enabled' }
    },
    {
      api: 'cancelHostMaintenance',
      icon: 'minus-square',
      label: 'label.action.cancel.maintenance.mode',
      message: 'message.action.cancel.maintenance.mode',
      docHelp: 'adminguide/hosts.html#maintaining-hypervisors-on-hosts',
      dataView: true,
      show: (record) => { return record.resourcestate === 'Maintenance' || record.resourcestate === 'ErrorInMaintenance' || record.resourcestate === 'PrepareForMaintenance' }
    },
    {
      api: 'configureOutOfBandManagement',
      icon: 'setting',
      label: 'label.outofbandmanagement.configure',
      message: 'label.outofbandmanagement.configure',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      args: ['hostid', 'address', 'port', 'username', 'password', 'driver'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        },
        driver: {
          options: ['ipmitool', 'nestedcloudstack', 'redfish']
        }
      }
    },
    {
      api: 'enableOutOfBandManagementForHost',
      icon: 'plus-circle',
      label: 'label.outofbandmanagement.enable',
      message: 'label.outofbandmanagement.enable',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return !(record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true)
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'disableOutOfBandManagementForHost',
      icon: 'minus-circle',
      label: 'label.outofbandmanagement.disable',
      message: 'label.outofbandmanagement.disable',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'issueOutOfBandManagementPowerAction',
      icon: 'login',
      label: 'label.outofbandmanagement.action.issue',
      message: 'label.outofbandmanagement.action.issue',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true
      },
      args: ['hostid', 'action'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'changeOutOfBandManagementPassword',
      icon: 'key',
      label: 'label.outofbandmanagement.changepassword',
      message: 'label.outofbandmanagement.changepassword',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true
      },
      args: ['hostid', 'password'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'configureHAForHost',
      icon: 'tool',
      label: 'label.ha.configure',
      message: 'label.ha.configure',
      docHelp: 'adminguide/reliability.html#ha-for-hosts',
      dataView: true,
      show: (record) => { return ['KVM', 'Simulator'].includes(record.hypervisor) },
      args: ['hostid', 'provider'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        },
        provider: {
          options: ['KVMHAProvider']
        }
      }
    },
    {
      api: 'enableHAForHost',
      icon: 'eye',
      label: 'label.ha.enable',
      message: 'label.ha.enable',
      docHelp: 'adminguide/reliability.html#ha-for-hosts',
      dataView: true,
      show: (record) => {
        return !(record.hostha && record.hostha.haenable &&
          record.hostha.haenable === true)
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'disableHAForHost',
      icon: 'eye-invisible',
      label: 'label.ha.disable',
      message: 'label.ha.disable',
      docHelp: 'adminguide/reliability.html#ha-for-hosts',
      dataView: true,
      show: (record) => {
        return record.hostha && record.hostha.haenable &&
        record.hostha.haenable === true
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'startRollingMaintenance',
      icon: 'setting',
      label: 'label.start.rolling.maintenance',
      message: 'label.start.rolling.maintenance',
      docHelp: 'adminguide/hosts.html#kvm-rolling-maintenance',
      dataView: true,
      show: (record) => {
        return record.hypervisor === 'KVM' && (record.resourcestate === 'Enabled' || record.resourcestate === 'ErrorInMaintenance')
      },
      args: ['timeout', 'payload', 'forced', 'hostids'],
      mapping: {
        hostids: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'deleteHost',
      icon: 'delete',
      label: 'label.action.remove.host',
      docHelp: 'adminguide/hosts.html#removing-hosts',
      dataView: true,
      args: ['forced'],
      show: (record) => { return ['Maintenance', 'Disabled', 'Down', 'Alert', 'Disconnected'].includes(record.resourcestate) }
    }
  ]
}
<commit_msg>Add options for OOBM action commands (#894)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import store from '@/store'

export default {
  name: 'host',
  title: 'label.hosts',
  icon: 'desktop',
  permission: ['listHostsMetrics'],
  resourceType: 'Host',
  params: { type: 'routing' },
  columns: () => {
    const fields = ['name', 'state', 'resourcestate', 'ipaddress', 'hypervisor', 'instances', 'powerstate']
    const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite']
    if (store.getters.metrics) {
      fields.push(...metricsFields)
    }
    fields.push('clustername')
    fields.push('zonename')
    return fields
  },
  details: ['name', 'id', 'resourcestate', 'ipaddress', 'hypervisor', 'type', 'clustername', 'podname', 'zonename', 'disconnected', 'created'],
  tabs: [{
    name: 'details',
    component: () => import('@/components/view/DetailsTab.vue')
  }],
  related: [{
    name: 'vm',
    title: 'label.instances',
    param: 'hostid'
  }],
  actions: [
    {
      api: 'addHost',
      icon: 'plus',
      label: 'label.add.host',
      docHelp: 'adminguide/installguide/configuration.html#adding-a-host',
      listView: true,
      popup: true,
      component: () => import('@/views/infra/HostAdd.vue')
    },
    {
      api: 'updateHost',
      icon: 'edit',
      label: 'label.edit',
      dataView: true,
      args: ['name', 'hosttags', 'oscategoryid'],
      mapping: {
        oscategoryid: {
          api: 'listOsCategories'
        }
      }
    },
    {
      api: 'provisionCertificate',
      icon: 'safety-certificate',
      label: 'label.action.secure.host',
      message: 'message.action.secure.host',
      dataView: true,
      show: (record) => { return record.hypervisor === 'KVM' },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'reconnectHost',
      icon: 'forward',
      label: 'label.action.force.reconnect',
      message: 'message.confirm.action.force.reconnect',
      dataView: true,
      show: (record) => { return ['Disconnected', 'Up'].includes(record.state) }
    },
    {
      api: 'updateHost',
      icon: 'pause-circle',
      label: 'label.disable.host',
      message: 'message.confirm.disable.host',
      dataView: true,
      defaultArgs: { allocationstate: 'Disable' },
      show: (record) => { return record.resourcestate === 'Enabled' }
    },
    {
      api: 'updateHost',
      icon: 'play-circle',
      label: 'label.enable.host',
      message: 'message.confirm.enable.host',
      dataView: true,
      defaultArgs: { allocationstate: 'Enable' },
      show: (record) => { return record.resourcestate === 'Disabled' }
    },
    {
      api: 'prepareHostForMaintenance',
      icon: 'plus-square',
      label: 'label.action.enable.maintenance.mode',
      message: 'message.action.host.enable.maintenance.mode',
      docHelp: 'adminguide/hosts.html#maintaining-hypervisors-on-hosts',
      dataView: true,
      show: (record) => { return record.resourcestate === 'Enabled' }
    },
    {
      api: 'cancelHostMaintenance',
      icon: 'minus-square',
      label: 'label.action.cancel.maintenance.mode',
      message: 'message.action.cancel.maintenance.mode',
      docHelp: 'adminguide/hosts.html#maintaining-hypervisors-on-hosts',
      dataView: true,
      show: (record) => { return record.resourcestate === 'Maintenance' || record.resourcestate === 'ErrorInMaintenance' || record.resourcestate === 'PrepareForMaintenance' }
    },
    {
      api: 'configureOutOfBandManagement',
      icon: 'setting',
      label: 'label.outofbandmanagement.configure',
      message: 'label.outofbandmanagement.configure',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      args: ['hostid', 'address', 'port', 'username', 'password', 'driver'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        },
        driver: {
          options: ['ipmitool', 'nestedcloudstack', 'redfish']
        }
      }
    },
    {
      api: 'enableOutOfBandManagementForHost',
      icon: 'plus-circle',
      label: 'label.outofbandmanagement.enable',
      message: 'label.outofbandmanagement.enable',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return !(record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true)
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'disableOutOfBandManagementForHost',
      icon: 'minus-circle',
      label: 'label.outofbandmanagement.disable',
      message: 'label.outofbandmanagement.disable',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'issueOutOfBandManagementPowerAction',
      icon: 'login',
      label: 'label.outofbandmanagement.action.issue',
      message: 'label.outofbandmanagement.action.issue',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true
      },
      args: ['hostid', 'action'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        },
        action: {
          options: ['ON', 'OFF', 'CYCLE', 'RESET', 'SOFT', 'STATUS']
        }
      }
    },
    {
      api: 'changeOutOfBandManagementPassword',
      icon: 'key',
      label: 'label.outofbandmanagement.changepassword',
      message: 'label.outofbandmanagement.changepassword',
      docHelp: 'adminguide/hosts.html#out-of-band-management',
      dataView: true,
      show: (record) => {
        return record.outofbandmanagement && record.outofbandmanagement.enabled &&
          record.outofbandmanagement.enabled === true
      },
      args: ['hostid', 'password'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'configureHAForHost',
      icon: 'tool',
      label: 'label.ha.configure',
      message: 'label.ha.configure',
      docHelp: 'adminguide/reliability.html#ha-for-hosts',
      dataView: true,
      show: (record) => { return ['KVM', 'Simulator'].includes(record.hypervisor) },
      args: ['hostid', 'provider'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        },
        provider: {
          options: ['KVMHAProvider']
        }
      }
    },
    {
      api: 'enableHAForHost',
      icon: 'eye',
      label: 'label.ha.enable',
      message: 'label.ha.enable',
      docHelp: 'adminguide/reliability.html#ha-for-hosts',
      dataView: true,
      show: (record) => {
        return !(record.hostha && record.hostha.haenable &&
          record.hostha.haenable === true)
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'disableHAForHost',
      icon: 'eye-invisible',
      label: 'label.ha.disable',
      message: 'label.ha.disable',
      docHelp: 'adminguide/reliability.html#ha-for-hosts',
      dataView: true,
      show: (record) => {
        return record.hostha && record.hostha.haenable &&
        record.hostha.haenable === true
      },
      args: ['hostid'],
      mapping: {
        hostid: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'startRollingMaintenance',
      icon: 'setting',
      label: 'label.start.rolling.maintenance',
      message: 'label.start.rolling.maintenance',
      docHelp: 'adminguide/hosts.html#kvm-rolling-maintenance',
      dataView: true,
      show: (record) => {
        return record.hypervisor === 'KVM' && (record.resourcestate === 'Enabled' || record.resourcestate === 'ErrorInMaintenance')
      },
      args: ['timeout', 'payload', 'forced', 'hostids'],
      mapping: {
        hostids: {
          value: (record) => { return record.id }
        }
      }
    },
    {
      api: 'deleteHost',
      icon: 'delete',
      label: 'label.action.remove.host',
      docHelp: 'adminguide/hosts.html#removing-hosts',
      dataView: true,
      args: ['forced'],
      show: (record) => { return ['Maintenance', 'Disabled', 'Down', 'Alert', 'Disconnected'].includes(record.resourcestate) }
    }
  ]
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2016 Luminarys <postmaster@gensok.io>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

document.addEventListener('DOMContentLoaded', function() {
  /**
   * Sets up the elements inside file upload rows.
   *
   * @param {File} file
   * @return {HTMLLIElement} row
   */
  function addRow(file) {
    var row = document.createElement('li');

    var name = document.createElement('span');
    name.textContent = file.name;
    name.className = 'file-name';

    var progressIndicator = document.createElement('span');
    progressIndicator.className = 'progress-percent';
    progressIndicator.textContent = '0%';

    var progressBar = document.createElement('progress');
    progressBar.className = 'file-progress';
    progressBar.setAttribute('max', '100');
    progressBar.setAttribute('value', '0');

    row.appendChild(name);
    row.appendChild(progressBar);
    row.appendChild(progressIndicator);

    document.getElementById('upload-filelist').appendChild(row);
    return row;
  }

  /**
   * Updates the page while the file is being uploaded.
   *
   * @param {ProgressEvent} evt
   */
  function handleUploadProgress(evt) {
    var xhr = evt.target;
    var bar = xhr.bar;
    var percentIndicator = xhr.percent;

    /* If we have amounts of work done/left that we can calculate with
       (which, unless we're uploading dynamically resizing data, is always), calculate the percentage. */
    if (evt.lengthComputable) {
      var progressPercent = Math.floor((evt.loaded / evt.total) * 100);
      bar.setAttribute('value', progressPercent);
      percentIndicator.textContent = progressPercent + '%';
    }
  }

  /**
   * Complete the uploading process by checking the response status and, if the
   * upload was successful, writing the URL(s) and creating the copy element(s)
   * for the files.
   *
   * @param {ProgressEvent} evt
   */
  function handleUploadComplete(evt) {
    var xhr = evt.target;
    var bar = xhr.bar;
    var row = xhr.row;
    var percentIndicator = xhr.percent;

    percentIndicator.style.visibility = 'hidden';
    bar.style.visibility = 'hidden';
    row.removeChild(bar);
    row.removeChild(percentIndicator);
    var respStatus = xhr.status;

    var url = document.createElement('span');
    url.className = 'file-url';
    row.appendChild(url);

    var link = document.createElement('a');
    if (respStatus === 200) {
      var response = JSON.parse(xhr.responseText);
      if (response.success) {
        link.textContent = response.files[0].url.replace(/.*?:\/\//g, '');
        link.href = response.files[0].url;
        url.appendChild(link);
        var copy = document.createElement('button');
        copy.className = 'upload-clipboard-btn';
        var glyph = document.createElement('img');
        glyph.src = '/assets/img/glyphicons-512-copy.png';
        copy.appendChild(glyph);
        url.appendChild(copy);
        copy.addEventListener("click", function(event) {
          /* Why create an element?  The text needs to be on screen to be
             selected and thus copied. The only text we have on-screen is the link
             without the http[s]:// part. So, this creates an element with the
             full link for a moment and then deletes it.

             See the "Complex Example: Copy to clipboard without displaying
             input" section at: https://stackoverflow.com/a/30810322 */
          var element = document.createElement('a');
          element.textContent = response.files[0].url;
          link.appendChild(element);
          var range = document.createRange();
          range.selectNode(element);
          window.getSelection().removeAllRanges();
          window.getSelection().addRange(range);
          document.execCommand("copy");
          link.removeChild(element);
        });
      } else {
        bar.innerHTML = 'Error: ' + response.description;
      }
    } else {
      var response = JSON.parse(xhr.responseText);
      link.textContent = response.description;
      url.appendChild(link);
    }
  }

  /**
   * Updates the page while the file is being uploaded.
   *
   * @param {File} file
   * @param {HTMLLIElement} row
   */
  function uploadFile(file, row) {
    var bar = row.querySelector('.file-progress');
    var percentIndicator = row.querySelector('.progress-percent');
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'upload.php');

    xhr['row'] = row;
    xhr['bar'] = bar;
    xhr['percent'] = percentIndicator;
    xhr.upload['bar'] = bar;
    xhr.upload['percent'] = percentIndicator;

    xhr.addEventListener('load', handleUploadComplete, false);
    xhr.upload.onprogress = handleUploadProgress;
    var form = new FormData();
    form.append('files[]', file);
    xhr.send(form);
  }

  /**
   * Prevents the browser for allowing the normal actions associated with an event.
   * This is used by event handlers to allow custom functionality without
   * having to worry about the other consequences of that action.
   *
   * @param {Event} evt
   */
  function stopDefaultEvent(evt) {
    evt.stopPropagation();
    evt.preventDefault();
  }

  /**
   * Adds 1 to the state and changes the text.
   *
   * @param {Object} state
   * @param {HTMLButtonElement} element
   * @param {DragEvent} evt
   */
  function handleDrag(state, element, evt) {
    stopDefaultEvent(evt);
    if (state.dragCount == 1) {
      element.textContent = 'Put it in me Senpai~';
    }
    state.dragCount += 1;
  }

  /**
   * Subtracts 1 from the state and changes the text back.
   *
   * @param {Object} state
   * @param {HTMLButtonElement} element
   * @param {DragEvent} evt
   */
  function handleDragAway(state, element, evt) {
    stopDefaultEvent(evt);
    state.dragCount -= 1;
    if (state.dragCount == 0) {
      element.textContent = 'Select or drop file(s)';
    }
  }

  /**
   * Prepares files for uploading after being added via drag-drop.
   *
   * @param {Object} state
   * @param {HTMLButtonElement} element
   * @param {DragEvent} evt
   */
  function handleDragDrop(state, element, evt) {
    stopDefaultEvent(evt);
    handleDragAway(state, element, evt);
    var len = evt.dataTransfer.files.length;
    for (var i = 0; i < len; i++) {
      var file = evt.dataTransfer.files[i];
      var row = addRow(file);
      uploadFile(file, row);
    }
  }

  /**
   * Prepares the files to be uploaded when they're added to the <input> element.
   *
   * @param {InputEvent} evt
   */
  function uploadFiles(evt) {
    var len = evt.target.files.length;
    // For each file, make a row, and upload the file.
    for (var i = 0; i < len; i++) {
      var file = evt.target.files[i];
      var row = addRow(file);
      uploadFile(file, row);
    }
  }

  /**
   * Opens up a "Select files.." dialog window to allow users to select files to upload.
   *
   * @param {HTMLInputElement} target
   * @param {InputEvent} evt
   */
  function selectFiles(target, evt) {
    stopDefaultEvent(evt);
    target.click();
  }

  /* Set-up the event handlers for the <button>, <input> and the window itself
     and also set the "js" class on selector "#upload-form", presumably to
     allow custom styles for clients running javascript. */
  var state = { dragCount: 0 };
  var uploadButton = document.getElementById('upload-btn');
  window.addEventListener('dragenter', handleDrag.bind(this, state, uploadButton), false);
  window.addEventListener('dragleave', handleDragAway.bind(this, state, uploadButton), false);
  window.addEventListener('drop', handleDragAway.bind(this, state, uploadButton), false);
  window.addEventListener('dragover', stopDefaultEvent, false);

  var uploadForm = document.getElementById('upload-form');
  var uploadInput = document.getElementById('upload-input');
  uploadInput.addEventListener('change', uploadFiles);
  uploadButton.addEventListener('click', selectFiles.bind(this, uploadInput));
  uploadButton.addEventListener('drop', handleDragDrop.bind(this, state, uploadButton), false);
  uploadForm.classList.add('js');
});
<commit_msg>Add proper filesize limit text<commit_after>/**
 * Copyright (c) 2016 Luminarys <postmaster@gensok.io>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

document.addEventListener('DOMContentLoaded', function() {
  /**
   * Sets up the elements inside file upload rows.
   *
   * @param {File} file
   * @return {HTMLLIElement} row
   */
  function addRow(file) {
    var row = document.createElement('li');

    var name = document.createElement('span');
    name.textContent = file.name;
    name.className = 'file-name';

    var progressIndicator = document.createElement('span');
    progressIndicator.className = 'progress-percent';
    progressIndicator.textContent = '0%';

    var progressBar = document.createElement('progress');
    progressBar.className = 'file-progress';
    progressBar.setAttribute('max', '100');
    progressBar.setAttribute('value', '0');

    row.appendChild(name);
    row.appendChild(progressBar);
    row.appendChild(progressIndicator);

    document.getElementById('upload-filelist').appendChild(row);
    return row;
  }

  /**
   * Updates the page while the file is being uploaded.
   *
   * @param {ProgressEvent} evt
   */
  function handleUploadProgress(evt) {
    var xhr = evt.target;
    var bar = xhr.bar;
    var percentIndicator = xhr.percent;

    /* If we have amounts of work done/left that we can calculate with
       (which, unless we're uploading dynamically resizing data, is always), calculate the percentage. */
    if (evt.lengthComputable) {
      var progressPercent = Math.floor((evt.loaded / evt.total) * 100);
      bar.setAttribute('value', progressPercent);
      percentIndicator.textContent = progressPercent + '%';
    }
  }

  /**
   * Complete the uploading process by checking the response status and, if the
   * upload was successful, writing the URL(s) and creating the copy element(s)
   * for the files.
   *
   * @param {ProgressEvent} evt
   */
  function handleUploadComplete(evt) {
    var xhr = evt.target;
    var bar = xhr.bar;
    var row = xhr.row;
    var percentIndicator = xhr.percent;

    percentIndicator.style.visibility = 'hidden';
    bar.style.visibility = 'hidden';
    row.removeChild(bar);
    row.removeChild(percentIndicator);
    var respStatus = xhr.status;

    var url = document.createElement('span');
    url.className = 'file-url';
    row.appendChild(url);

    var link = document.createElement('a');
    if (respStatus === 200) {
      var response = JSON.parse(xhr.responseText);
      if (response.success) {
        link.textContent = response.files[0].url.replace(/.*?:\/\//g, '');
        link.href = response.files[0].url;
        url.appendChild(link);
        var copy = document.createElement('button');
        copy.className = 'upload-clipboard-btn';
        var glyph = document.createElement('img');
        glyph.src = '/assets/img/glyphicons-512-copy.png';
        copy.appendChild(glyph);
        url.appendChild(copy);
        copy.addEventListener("click", function(event) {
          /* Why create an element?  The text needs to be on screen to be
             selected and thus copied. The only text we have on-screen is the link
             without the http[s]:// part. So, this creates an element with the
             full link for a moment and then deletes it.

             See the "Complex Example: Copy to clipboard without displaying
             input" section at: https://stackoverflow.com/a/30810322 */
          var element = document.createElement('a');
          element.textContent = response.files[0].url;
          link.appendChild(element);
          var range = document.createRange();
          range.selectNode(element);
          window.getSelection().removeAllRanges();
          window.getSelection().addRange(range);
          document.execCommand("copy");
          link.removeChild(element);
        });
      } else {
        bar.innerHTML = 'Error: ' + response.description;
      }
    } else {
      var response = JSON.parse(xhr.responseText);
      if (response.description == 'The uploaded file exceeds the upload_max_filesize directive in php.ini')
        link.textContent = 'File too large!';
      else
        link.textContent = 'Internal Server Error!';
      url.appendChild(link);
    }
  }

  /**
   * Updates the page while the file is being uploaded.
   *
   * @param {File} file
   * @param {HTMLLIElement} row
   */
  function uploadFile(file, row) {
    var bar = row.querySelector('.file-progress');
    var percentIndicator = row.querySelector('.progress-percent');
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'upload.php');

    xhr['row'] = row;
    xhr['bar'] = bar;
    xhr['percent'] = percentIndicator;
    xhr.upload['bar'] = bar;
    xhr.upload['percent'] = percentIndicator;

    xhr.addEventListener('load', handleUploadComplete, false);
    xhr.upload.onprogress = handleUploadProgress;
    var form = new FormData();
    form.append('files[]', file);
    xhr.send(form);
  }

  /**
   * Prevents the browser for allowing the normal actions associated with an event.
   * This is used by event handlers to allow custom functionality without
   * having to worry about the other consequences of that action.
   *
   * @param {Event} evt
   */
  function stopDefaultEvent(evt) {
    evt.stopPropagation();
    evt.preventDefault();
  }

  /**
   * Adds 1 to the state and changes the text.
   *
   * @param {Object} state
   * @param {HTMLButtonElement} element
   * @param {DragEvent} evt
   */
  function handleDrag(state, element, evt) {
    stopDefaultEvent(evt);
    if (state.dragCount == 1) {
      element.textContent = 'Put it in me Senpai~';
    }
    state.dragCount += 1;
  }

  /**
   * Subtracts 1 from the state and changes the text back.
   *
   * @param {Object} state
   * @param {HTMLButtonElement} element
   * @param {DragEvent} evt
   */
  function handleDragAway(state, element, evt) {
    stopDefaultEvent(evt);
    state.dragCount -= 1;
    if (state.dragCount == 0) {
      element.textContent = 'Select or drop file(s)';
    }
  }

  /**
   * Prepares files for uploading after being added via drag-drop.
   *
   * @param {Object} state
   * @param {HTMLButtonElement} element
   * @param {DragEvent} evt
   */
  function handleDragDrop(state, element, evt) {
    stopDefaultEvent(evt);
    handleDragAway(state, element, evt);
    var len = evt.dataTransfer.files.length;
    for (var i = 0; i < len; i++) {
      var file = evt.dataTransfer.files[i];
      var row = addRow(file);
      uploadFile(file, row);
    }
  }

  /**
   * Prepares the files to be uploaded when they're added to the <input> element.
   *
   * @param {InputEvent} evt
   */
  function uploadFiles(evt) {
    var len = evt.target.files.length;
    // For each file, make a row, and upload the file.
    for (var i = 0; i < len; i++) {
      var file = evt.target.files[i];
      var row = addRow(file);
      uploadFile(file, row);
    }
  }

  /**
   * Opens up a "Select files.." dialog window to allow users to select files to upload.
   *
   * @param {HTMLInputElement} target
   * @param {InputEvent} evt
   */
  function selectFiles(target, evt) {
    stopDefaultEvent(evt);
    target.click();
  }

  /* Set-up the event handlers for the <button>, <input> and the window itself
     and also set the "js" class on selector "#upload-form", presumably to
     allow custom styles for clients running javascript. */
  var state = { dragCount: 0 };
  var uploadButton = document.getElementById('upload-btn');
  window.addEventListener('dragenter', handleDrag.bind(this, state, uploadButton), false);
  window.addEventListener('dragleave', handleDragAway.bind(this, state, uploadButton), false);
  window.addEventListener('drop', handleDragAway.bind(this, state, uploadButton), false);
  window.addEventListener('dragover', stopDefaultEvent, false);

  var uploadForm = document.getElementById('upload-form');
  var uploadInput = document.getElementById('upload-input');
  uploadInput.addEventListener('change', uploadFiles);
  uploadButton.addEventListener('click', selectFiles.bind(this, uploadInput));
  uploadButton.addEventListener('drop', handleDragDrop.bind(this, state, uploadButton), false);
  uploadForm.classList.add('js');
});
<|endoftext|>


<commit_before>/**
 * Default model configuration
 * (sails.config.models)
 *
 * Unless you override them, the following properties will be included
 * in each of your models.
 *
 * For more info on Sails models, see:
 * http://sailsjs.org/#!/documentation/concepts/ORM
 */

module.exports.models = {

  /***************************************************************************
  *                                                                          *
  * Your app's default connection. i.e. the name of one of your app's        *
  * connections (see `config/connections.js`)                                *
  *                                                                          *
  ***************************************************************************/
  // connection: 'localDiskDb',

  /***************************************************************************
  *                                                                          *
  * How and whether Sails will attempt to automatically rebuild the          *
  * tables/collections/etc. in your schema.                                  *
  *                                                                          *
  * See http://sailsjs.org/#!/documentation/concepts/ORM/model-settings.html  *
  *                                                                          *
  ***************************************************************************/
  // migrate: 'alter'

};
<commit_msg>Default model migrate to alter<commit_after>/**
 * Default model configuration
 * (sails.config.models)
 *
 * Unless you override them, the following properties will be included
 * in each of your models.
 *
 * For more info on Sails models, see:
 * http://sailsjs.org/#!/documentation/concepts/ORM
 */

module.exports.models = {

  /***************************************************************************
  *                                                                          *
  * Your app's default connection. i.e. the name of one of your app's        *
  * connections (see `config/connections.js`)                                *
  *                                                                          *
  ***************************************************************************/
  // connection: 'localDiskDb',

  /***************************************************************************
  *                                                                          *
  * How and whether Sails will attempt to automatically rebuild the          *
  * tables/collections/etc. in your schema.                                  *
  *                                                                          *
  * See http://sailsjs.org/#!/documentation/concepts/ORM/model-settings.html  *
  *                                                                          *
  ***************************************************************************/
  migrate: 'alter'

};
<|endoftext|>


<commit_before>/**
 * Mana(gement) page handler.
 * Called as mana. Because I like magic ;p.
 *
 * @module sms-library-helper/handlers/api
 * @author Holi0317 <holliswuhollis@gmail.com>
 * @license MIT
 */

'use strict';

let google = require('googleapis');
let Promise = require('bluebird');

let config = require('../../config');
let models = require('../models');
let utils = require('../utils');

/**
 * Check if user is qualified for accessing the mana page.
 * Return a 404 page is user is not qualified.
 */
module.exports.middleware = function (req, res, next) {
  // Reject if not logined.
  if (!req.logined) {
    return res.status(404).render('error', {code: 404, message: 'Page not found'});
  }

  // Populate user from database.
  models.user.findOne({
    googleId: req.session.googleId
  })
    .then(result => {
      if (result.googleId === config.adminID) {
        result.isAdmin = true
      }
      if (!result.isAdmin) {
        res.status(404).render('error', {code: 404, message: 'Page not found'});
        throw new utils.BreakSignal();
      }
      req.user = result;
      return result.save();
    })
    .then(() => {
      next();
      return Promise.resolve();
    })
    .catch(err => {
      if (err instanceof utils.BreakSignal) {
        return;
      } else {
        throw err;
      }
    })
};

module.exports.index = function (req, res) {
  models.user.find()
  .sort({
    googleId: -1
  })
    .then(result => {
      res.render('mana', {data: result})
    });
}
module.exports.getUser = function (req, res) {
  let databaseRes;
  models.user.findOne({
    googleId: req.params.user
  })
    .then(result => {
      if (!result) {
        res.render('mana-no-user');
        throw new utils.BreakSignal();
      }
      databaseRes = result;

      let plus = google.plus('v1');
      let getAsync = Promise.promisify(plus.people.get);
      let oauth2client = utils.oauth2clientFactory();
      oauth2client.setCredentials(result.tokens);

      return getAsync({userId: 'me', auth: oauth2client});
    })
    .then(googleRes => {
      res.render('mana-user', {data: databaseRes, name: googleRes.displayName});
    })
    .catch(err => {
      if (err instanceof utils.BreakSignal) {
        return
      } else {
        res.status(500).render('error');
      }
    })
}
<commit_msg>Add some documentation for mana.<commit_after>/**
 * Mana(gement) page handler.
 * Called as mana. Because I like magic ;p.
 *
 * @module sms-library-helper/handlers/api
 * @author Holi0317 <holliswuhollis@gmail.com>
 * @license MIT
 *
 * @requires googleapis
 * @requires bluebird
 */

'use strict';

let google = require('googleapis');
let Promise = require('bluebird');

let config = require('../../config');
let models = require('../models');
let utils = require('../utils');

/**
 * Check if user is qualified for accessing the mana page.
 * Return a 404 page is user is not qualified.
 */
module.exports.middleware = function (req, res, next) {
  // Reject if not logined.
  if (!req.logined) {
    return res.status(404).render('error', {code: 404, message: 'Page not found'});
  }

  // Populate user from database.
  models.user.findOne({
    googleId: req.session.googleId
  })
    .then(result => {
      if (result.googleId === config.adminID) {
        result.isAdmin = true
      }
      if (!result.isAdmin) {
        res.status(404).render('error', {code: 404, message: 'Page not found'});
        throw new utils.BreakSignal();
      }
      req.user = result;
      return result.save();
    })
    .then(() => {
      next();
      return Promise.resolve();
    })
    .catch(err => {
      if (err instanceof utils.BreakSignal) {
        return;
      } else {
        throw err;
      }
    })
};

/**
 * Query from database and render management page.
 */
module.exports.index = function (req, res) {
  models.user.find()
  .sort({
    googleId: -1
  })
    .then(result => {
      res.render('mana', {data: result})
    });
}

/**
 * Query database, get user name from Google and render user management page.
 */
module.exports.getUser = function (req, res) {
  let databaseRes;
  models.user.findOne({
    googleId: req.params.user
  })
    .then(result => {
      if (!result) {
        res.render('mana-no-user');
        throw new utils.BreakSignal();
      }
      databaseRes = result;

      let plus = google.plus('v1');
      let getAsync = Promise.promisify(plus.people.get);
      let oauth2client = utils.oauth2clientFactory();
      oauth2client.setCredentials(result.tokens);

      return getAsync({userId: 'me', auth: oauth2client});
    })
    .then(googleRes => {
      res.render('mana-user', {data: databaseRes, name: googleRes.displayName});
    })
    .catch(err => {
      if (err instanceof utils.BreakSignal) {
        return
      } else {
        res.status(500).render('error');
      }
    })
}
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  connection.query('SELECT * FROM comics', function (error, results, fields) {
    if(error){
      res.send(JSON.stringify({"status": 500, "error": error, "response": null}));
      //If there is error, we send the error in the error section with 500 status
    } else {
      res.send(JSON.stringify({"status": 200, "error": null, "response": results}));
      //If there is no error, all is good and response is 200OK.
    }
  });
});

router.get('/:slug', function(req, res, next) {
  console.log(req.params.slug);
  connection.query('SELECT * FROM comics WHERE slug =' + req.params.slug, function (error, results, fields) {
    if(error){
      res.send(JSON.stringify({"status": 500, "error": error, "response": null}));
      //If there is error, we send the error in the error section with 500 status
    } else {
      res.send(JSON.stringify({"status": 200, "error": null, "response": results}));
      //If there is no error, all is good and response is 200OK.
    }
  });
});

module.exports = router;
<commit_msg>fixed<commit_after>var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  connection.query('SELECT * FROM comics', function (error, results, fields) {
    if(error){
      res.send(JSON.stringify({"status": 500, "error": error, "response": null}));
      //If there is error, we send the error in the error section with 500 status
    } else {
      res.send(JSON.stringify({"status": 200, "error": null, "response": results}));
      //If there is no error, all is good and response is 200OK.
    }
  });
});

router.get('/:slug', function(req, res, next) {
  console.log(req.params.slug);
  connection.query('SELECT * FROM comics WHERE slug ="' + req.params.slug + '"', function (error, results, fields) {
    if(error){
      res.send(JSON.stringify({"status": 500, "error": error, "response": null}));
      //If there is error, we send the error in the error section with 500 status
    } else {
      res.send(JSON.stringify({"status": 200, "error": null, "response": results}));
      //If there is no error, all is good and response is 200OK.
    }
  });
});

module.exports = router;
<|endoftext|>


<commit_before>/*
 * Main server bootstrap file for the API server.
 *
 * (C) 2015, 2016, 2017, 2018 TekMonks. All rights reserved.
 * License: MIT - see enclosed LICENSE file.
 */

global.CONSTANTS = require(__dirname + "/lib/constants.js");
const crypt = require(CONSTANTS.LIBDIR+"/crypt.js");
const utils = require(CONSTANTS.LIBDIR+"/utils.js");
const urlMod = require("url");

exports.bootstrap = bootstrap;

// support starting in stand-alone config
if (require("cluster").isMaster == true) bootstrap();

function bootstrap() {
	/* Init - Server bootup */
	console.log("Starting...");

	/* Init the logs */
	console.log("Initializing the logs.");
	require(CONSTANTS.LIBDIR+"/log.js").initGlobalLoggerSync(CONSTANTS.LOGMAIN);
	/* Init the API registry */
	LOG.info("Initializing the API registry.");
	require(CONSTANTS.LIBDIR+"/apiregistry.js").initSync();

	/* Run the server */
	initAndRunTransportLoop();
}

function initAndRunTransportLoop() {
	/* Init the transport */
	let transport = require(CONSTANTS.TRANSPORT);
	let server = require(CONSTANTS.LIBDIR+"/"+transport.servertype+".js");
	server.initSync(transport.port, transport.host||"::");

	console.log(`Server started on ${transport.host||"::"}:${transport.port}`);
	LOG.info(`Server started on ${transport.host||"::"}:${transport.port}`);

	/* Override the console now - to our log file*/
	LOG.overrideConsole();

	/* Server loop */
	/* send request to the service mentioned in url*/
	server.connection.on("request", (req, res) => {
		let data = "";

		req.on("data", chunk => data+=chunk);

		req.on("end", async _ => {
			let respObj = await doService(req.url, data);
			if (respObj) {
				LOG.info("Got result: " + LOG.truncate(JSON.stringify(respObj)));
				res.writeHead(200, {"Content-Type" : "application/json"});
				if (apiregistry.isEncrypted(urlMod.parse(req.url).pathname))
					res.write("{\"data\":\""+crypt.encrypt(JSON.stringify(respObj))+"\"}");
				else res.write(JSON.stringify(respObj));
				res.end();
			} else {
				LOG.info("Sending 404 for: " + req.url);
				res.writeHead(404, {"Content-Type": "text/plain"});
				res.write("404 Not Found\n");
				res.end();
			}
		});
	});
}

async function doService(url, data) {
	LOG.info("Got request for the url: " + url);

	let endPoint = urlMod.parse(url, true).pathname;
	let query = urlMod.parse(url, true).query;
	let api = apiregistry.getAPI(endPoint);
	LOG.info("Looked up service, calling: " + api);

	if (api) {
		let jsonObj = {};
		try {
			if (apiregistry.isGet(endPoint) && apiregistry.isEncrypted(endPoint))
				jsonObj = query.data ? utils.queryToObject(crypt.decrypt(query.data)) : {};
			else if (apiregistry.isGet(endPoint) && !apiregistry.isEncrypted(endPoint)) jsonObj = query;
			else if (apiregistry.isEncrypted(endPoint)) jsonObj = JSON.parse(crypt.decrypt(JSON.parse(data).data));
			else jsonObj = JSON.parse(data);
		} catch (err) {
			LOG.info("Input JSON parser error: " + err);
			LOG.info("Bad JSON input, calling with empty object: " + url);
		}

		if (!apiregistry.checkKey(endPoint, jsonObj)) {LOG.error("Bad API key: "+url); return CONSTANTS.FALSE_RESULT;}
		else return await require(api).doService(jsonObj);
	}
	else LOG.info("API not found: " + url);
}

<commit_msg>Handle unexpected API errors and log.<commit_after>/*
 * Main server bootstrap file for the API server.
 *
 * (C) 2015, 2016, 2017, 2018 TekMonks. All rights reserved.
 * License: MIT - see enclosed LICENSE file.
 */

global.CONSTANTS = require(__dirname + "/lib/constants.js");
const crypt = require(CONSTANTS.LIBDIR+"/crypt.js");
const utils = require(CONSTANTS.LIBDIR+"/utils.js");
const urlMod = require("url");

exports.bootstrap = bootstrap;

// support starting in stand-alone config
if (require("cluster").isMaster == true) bootstrap();

function bootstrap() {
	/* Init - Server bootup */
	console.log("Starting...");

	/* Init the logs */
	console.log("Initializing the logs.");
	require(CONSTANTS.LIBDIR+"/log.js").initGlobalLoggerSync(CONSTANTS.LOGMAIN);
	/* Init the API registry */
	LOG.info("Initializing the API registry.");
	require(CONSTANTS.LIBDIR+"/apiregistry.js").initSync();

	/* Run the server */
	initAndRunTransportLoop();
}

function initAndRunTransportLoop() {
	/* Init the transport */
	let transport = require(CONSTANTS.TRANSPORT);
	let server = require(CONSTANTS.LIBDIR+"/"+transport.servertype+".js");
	server.initSync(transport.port, transport.host||"::");

	console.log(`Server started on ${transport.host||"::"}:${transport.port}`);
	LOG.info(`Server started on ${transport.host||"::"}:${transport.port}`);

	/* Override the console now - to our log file*/
	LOG.overrideConsole();

	/* Server loop */
	/* send request to the service mentioned in url*/
	server.connection.on("request", (req, res) => {
		let data = "";

		req.on("data", chunk => data+=chunk);

		req.on("end", async _ => {
			let respObj = await doService(req.url, data);
			if (respObj) {
				LOG.info("Got result: " + LOG.truncate(JSON.stringify(respObj)));
				res.writeHead(200, {"Content-Type" : "application/json"});
				if (apiregistry.isEncrypted(urlMod.parse(req.url).pathname))
					res.write("{\"data\":\""+crypt.encrypt(JSON.stringify(respObj))+"\"}");
				else res.write(JSON.stringify(respObj));
				res.end();
			} else {
				LOG.info("Sending 404 for: " + req.url);
				res.writeHead(404, {"Content-Type": "text/plain"});
				res.write("404 Not Found\n");
				res.end();
			}
		});
	});
}

async function doService(url, data) {
	LOG.info("Got request for the url: " + url);

	let endPoint = urlMod.parse(url, true).pathname;
	let query = urlMod.parse(url, true).query;
	let api = apiregistry.getAPI(endPoint);
	LOG.info("Looked up service, calling: " + api);

	if (api) {
		let jsonObj = {};
		try {
			if (apiregistry.isGet(endPoint) && apiregistry.isEncrypted(endPoint))
				jsonObj = query.data ? utils.queryToObject(crypt.decrypt(query.data)) : {};
			else if (apiregistry.isGet(endPoint) && !apiregistry.isEncrypted(endPoint)) jsonObj = query;
			else if (apiregistry.isEncrypted(endPoint)) jsonObj = JSON.parse(crypt.decrypt(JSON.parse(data).data));
			else jsonObj = JSON.parse(data);
		} catch (err) {
			LOG.info("Input JSON parser error: " + err);
			LOG.info("Bad JSON input, calling with empty object: " + url);
		}

		if (!apiregistry.checkKey(endPoint, jsonObj)) {LOG.error("Bad API key: "+url); return CONSTANTS.FALSE_RESULT;}
		else try{return await require(api).doService(jsonObj);} catch (err) {LOG.debug(`API error: ${err}`); return CONSTANTS.FALSE_RESULT;}
	}
	else LOG.info("API not found: " + url);
}

<|endoftext|>