From fd22ef98dc492dfdf87f647a0eefcb828d499879 Mon Sep 17 00:00:00 2001 From: "Victor \"Nate\" Graf" Date: Sat, 8 Apr 2017 00:20:22 -0500 Subject: [PATCH] challenge update modal is now replaceable (#236) * challenge update modal is now replaceable By defining * [type]-challenge-update.hbs * [type]-challenge-modals.hbs * [type]-challenge-update.js in the /static/admin/js/templates/challenges/[type] folder the challenge update modal will be defined for any challenges of the given type. This allows for essentially full customizability of how you will edit custom challenge types in the admin UI. The reason for having two files, *update.hbs and *modals.hbs, is that *update.hbs defines the body for the main challenge update modal, while *modals.hbs defines any additional modals which will be used within the main modal There is one function which is required in *update.js is `openchal(id)` which will be passed the id of the challenge to be edited and should open the modal as well as load any needed data * fixed multi-modal issues Issues were coming from two sources: * I had placed the modals in an indirect relationship in the DOM tree. They need to be siblings I now see * There was double counting of modals within multi-modal.js. This only started to appear with the dynamically loaded modals. I fixed the script to accurately count modals each time --- .gitignore | 1 + CTFd/admin/challenges.py | 9 +- CTFd/static/admin/js/chal-new.js | 33 ++ CTFd/static/admin/js/chalboard.js | 387 +---------------- CTFd/static/admin/js/multi-modal.js | 23 +- .../standard/standard-challenge-modals.hbs | 409 ++++++++++++++++++ .../standard/standard-challenge-update.hbs | 0 .../standard/standard-challenge-update.js | 390 +++++++++++++++++ CTFd/static/admin/js/utils.js | 2 +- CTFd/templates/admin/chals.html | 331 +------------- 10 files changed, 871 insertions(+), 714 deletions(-) create mode 100644 CTFd/static/admin/js/chal-new.js create mode 100644 CTFd/static/admin/js/templates/challenges/standard/standard-challenge-modals.hbs create mode 100644 CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.hbs create mode 100644 CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.js diff --git a/.gitignore b/.gitignore index 54707f74..ce8bcb3b 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ CTFd/static/uploads CTFd/uploads .data/ .ctfd_secret_key +.*.swp diff --git a/CTFd/admin/challenges.py b/CTFd/admin/challenges.py index 8b733bcd..562bfaba 100644 --- a/CTFd/admin/challenges.py +++ b/CTFd/admin/challenges.py @@ -25,7 +25,7 @@ def admin_chal_types(): @admins_only def admin_chals(): if request.method == 'POST': - chals = Challenges.query.add_columns('id', 'name', 'value', 'description', 'category', 'hidden', 'max_attempts').order_by(Challenges.value).all() + chals = Challenges.query.add_columns('id', 'type', 'name', 'value', 'description', 'category', 'hidden', 'max_attempts').order_by(Challenges.value).all() teams_with_points = db.session.query(Solves.teamid).join(Teams).filter( Teams.banned == False).group_by(Solves.teamid).count() @@ -39,6 +39,9 @@ def admin_chals(): else: percentage = 0.0 + type_class = CHALLENGE_CLASSES.get(x.type) + type_name = type_class.name if type_class else None + json_data['game'].append({ 'id': x.id, 'name': x.name, @@ -47,6 +50,8 @@ def admin_chals(): 'category': x.category, 'hidden': x.hidden, 'max_attempts': x.max_attempts, + 'type': x.type, + 'type_name': type_name, 'percentage_solved': percentage }) @@ -286,4 +291,4 @@ def admin_update_chal(): db.session.add(challenge) db.session.commit() db.session.close() - return redirect(url_for('admin_challenges.admin_chals')) \ No newline at end of file + return redirect(url_for('admin_challenges.admin_chals')) diff --git a/CTFd/static/admin/js/chal-new.js b/CTFd/static/admin/js/chal-new.js new file mode 100644 index 00000000..b56b1bf1 --- /dev/null +++ b/CTFd/static/admin/js/chal-new.js @@ -0,0 +1,33 @@ +function load_chal_template(chal_type_name){ + $.get(script_root + '/static/admin/js/templates/challenges/'+ chal_type_name +'/' + chal_type_name + '-challenge-create.hbs', function(template_data){ + var template = Handlebars.compile(template_data); + $("#create-chal-entry-div").html(template({'nonce':nonce, 'script_root':script_root})); + $.getScript(script_root + '/static/admin/js/templates/challenges/'+chal_type_name+'/'+chal_type_name+'-challenge-create.js', function(){ + console.log('loaded'); + }); + }); +} + + +nonce = "{{ nonce }}"; +$.get(script_root + '/admin/chal_types', function(data){ + console.log(data); + $("#create-chals-select").empty(); + var chal_type_amt = Object.keys(data).length; + if (chal_type_amt > 1){ + var option = ""; + $("#create-chals-select").append(option); + for (var key in data){ + var option = "".format(key, data[key]); + $("#create-chals-select").append(option); + } + } else if (chal_type_amt == 1) { + var key = Object.keys(data)[0]; + $("#create-chals-select").parent().parent().parent().empty(); + load_chal_template(data[key]); + } +}); +$('#create-chals-select').change(function(){ + var chal_type_name = $(this).find("option:selected").text(); + load_chal_template(chal_type_name); +}); diff --git a/CTFd/static/admin/js/chalboard.js b/CTFd/static/admin/js/chalboard.js index 8a4c5f5b..71deb52d 100644 --- a/CTFd/static/admin/js/chalboard.js +++ b/CTFd/static/admin/js/chalboard.js @@ -38,255 +38,25 @@ function load_edit_key_modal(key_id, key_type_name) { }); } -function load_hint_modal(method, hintid){ - $('#hint-modal-hint').val(''); - $('#hint-modal-cost').val(''); - if (method == 'create'){ - $('#hint-modal-submit').attr('action', '/admin/hints'); - $('#hint-modal-title').text('Create Hint'); - $("#hint-modal").modal(); - } else if (method == 'update'){ - $.get(script_root + '/admin/hints/' + hintid, function(data){ - $('#hint-modal-submit').attr('action', '/admin/hints/' + hintid); - $('#hint-modal-hint').val(data.hint); - $('#hint-modal-cost').val(data.cost); - $('#hint-modal-title').text('Update Hint'); - $("#hint-modal-button").text('Update Hint'); - $("#hint-modal").modal(); - }); - } -} - -function loadchal(id, update) { - // $('#chal *').show() - // $('#chal > h1').hide() +function load_chal_template(id, success_cb){ obj = $.grep(challenges['game'], function (e) { return e.id == id; })[0] - $('#desc-write-link').click() // Switch to Write tab - $('.chal-title').text(obj.name); - $('.chal-name').val(obj.name); - $('.chal-desc').val(obj.description); - $('.chal-value').val(obj.value); - if (parseInt(obj.max_attempts) > 0){ - $('.chal-attempts').val(obj.max_attempts); - $('#limit_max_attempts').prop('checked', true); - $('#chal-attempts-group').show(); - } - $('.chal-category').val(obj.category); - $('.chal-id').val(obj.id); - $('.chal-hidden').prop('checked', false); - if (obj.hidden) { - $('.chal-hidden').prop('checked', true); - } - //$('#update-challenge .chal-delete').attr({ - // 'href': '/admin/chal/close/' + (id + 1) - //}) - if (typeof update === 'undefined') - $('#update-challenge').modal(); -} - -function submitkey(chal, key) { - $.post(script_root + "/admin/chal/" + chal, { - key: key, - nonce: $('#nonce').val() - }, function (data) { - alert(data) - }) -} - -function create_key(chal, key, key_type) { - $.post(script_root + "/admin/keys", { - chal: chal, - key: key, - key_type: key_type, - nonce: $('#nonce').val() - }, function (data) { - if (data == "1"){ - loadkeys(chal); - $("#create-keys").modal('toggle'); - } - }); -} - -function loadkeys(chal){ - $.get(script_root + '/admin/chal/' + chal + '/keys', function(data){ - $('#keys-chal').val(chal); - keys = $.parseJSON(JSON.stringify(data)); - keys = keys['keys']; - $('#current-keys').empty(); - $.get(script_root + "/static/admin/js/templates/admin-keys-table.hbs", function(data){ - var template = Handlebars.compile(data); - var wrapper = {keys: keys, script_root: script_root}; - $('#current-keys').append(template(wrapper)); + $.get(script_root + '/static/admin/js/templates/challenges/'+ obj['type_name'] +'/' + obj['type_name'] + '-challenge-update.hbs', function(template_data){ + var template = Handlebars.compile(template_data); + $.get(script_root + '/static/admin/js/templates/challenges/'+ obj['type_name'] +'/' + obj['type_name'] + '-challenge-modals.hbs', function(template_data){ + var template = Handlebars.compile(template_data); + $("#update-modals-entry-div").html(template({'nonce':$('#nonce').val(), 'script_root':script_root})); + }); + $.ajax({ + url: script_root + '/static/admin/js/templates/challenges/'+obj['type_name']+'/'+obj['type_name']+'-challenge-update.js', + dataType: "script", + success: success_cb, + cache: true, }); }); } -function updatekeys(){ - keys = []; - vals = []; - chal = $('#keys-chal').val() - $('.current-key').each(function(){ - keys.push($(this).val()); - }) - $('#current-keys input[name*="key_type"]:checked').each(function(){ - vals.push($(this).val()); - }) - $.post(script_root + '/admin/keys/'+chal, {'keys':keys, 'vals':vals, 'nonce': $('#nonce').val()}) - loadchal(chal, true) - $('#update-keys').modal('hide'); -} - - -function deletekey(key_id){ - $.post(script_root + '/admin/keys/'+key_id+'/delete', {'nonce': $('#nonce').val()}, function(data){ - if (data == "1") { - $('tr[name={0}]'.format(key_id)).remove(); - } - }); -} - -function updatekey(){ - var key_id = $('#key-id').val(); - var chal = $('#keys-chal').val(); - var key_data = $('#key-data').val(); - var key_type = $('#key-type').val(); - var nonce = $('#nonce').val(); - $.post(script_root + '/admin/keys/'+key_id, { - 'chal':chal, - 'key':key_data, - 'key_type': key_type, - 'nonce': nonce - }, function(data){ - if (data == "1") { - loadkeys(chal); - $('#edit-keys').modal('toggle'); - } - }) -} - -function loadtags(chal){ - $('#tags-chal').val(chal) - $('#current-tags').empty() - $('#chal-tags').empty() - $.get(script_root + '/admin/tags/'+chal, function(data){ - tags = $.parseJSON(JSON.stringify(data)) - tags = tags['tags'] - for (var i = 0; i < tags.length; i++) { - tag = ""+tags[i].tag+"×" - $('#current-tags').append(tag) - }; - $('.delete-tag').click(function(e){ - deletetag(e.target.name) - $(e.target).parent().remove() - }); - }); -} - -function deletetag(tagid){ - $.post(script_root + '/admin/tags/'+tagid+'/delete', {'nonce': $('#nonce').val()}); -} - - -function edithint(hintid){ - $.get(script_root + '/admin/hints/' + hintid, function(data){ - console.log(data); - }) -} - - -function deletehint(hintid){ - $.delete(script_root + '/admin/hints/' + hintid, function(data, textStatus, jqXHR){ - if (jqXHR.status == 204){ - var chalid = $('.chal-id').val(); - loadhints(chalid); - } - }); -} - - -function loadhints(chal){ - $.get(script_root + '/admin/chal/{0}/hints'.format(chal), function(data){ - var table = $('#hintsboard > tbody'); - table.empty(); - for (var i = 0; i < data.hints.length; i++) { - var hint = data.hints[i] - var hint_row = "" + - "{0}".format(hint.hint) + - "{0}".format(hint.cost) + - "" + - "".format(hint.id)+ - "".format(hint.id)+ - "" + - ""; - table.append(hint_row); - } - }); -} - - -function deletechal(chalid){ - $.post(script_root + '/admin/chal/delete', {'nonce':$('#nonce').val(), 'id':chalid}); -} - -function updatetags(){ - tags = []; - chal = $('#tags-chal').val() - $('#chal-tags > span > span').each(function(i, e){ - tags.push($(e).text()) - }); - $.post(script_root + '/admin/tags/'+chal, {'tags':tags, 'nonce': $('#nonce').val()}) - loadchal(chal) -} - -function updatefiles(){ - chal = $('#files-chal').val(); - var form = $('#update-files form')[0]; - var formData = new FormData(form); - $.ajax({ - url: script_root + '/admin/files/'+chal, - data: formData, - type: 'POST', - cache: false, - contentType: false, - processData: false, - success: function(data){ - form.reset(); - loadfiles(chal); - $('#update-files').modal('hide'); - } - }); -} - -function loadfiles(chal){ - $('#update-files form').attr('action', script_root+'/admin/files/'+chal) - $.get(script_root + '/admin/files/' + chal, function(data){ - $('#files-chal').val(chal) - files = $.parseJSON(JSON.stringify(data)); - files = files['files'] - $('#current-files').empty() - for(x=0; x'+''+filename+'Delete') - } - }); -} - -function deletefile(chal, file, elem){ - $.post(script_root + '/admin/files/' + chal,{ - 'nonce': $('#nonce').val(), - 'method': 'delete', - 'file': file - }, function (data){ - if (data == "1") { - elem.parent().remove() - } - }); -} - - function loadchals(){ $('#challenges').empty(); $.post(script_root + "/admin/chals", { @@ -310,11 +80,10 @@ function loadchals(){ }; $('#challenges button').click(function (e) { - loadchal(this.value); - loadkeys(this.value); - loadhints(this.value); - loadtags(this.value); - loadfiles(this.value); + id = this.value + load_chal_template(id, function(){ + openchal(id); + }); }); // $('.create-challenge').click(function (e) { @@ -326,130 +95,6 @@ function loadchals(){ }); } -$('#submit-key').click(function (e) { - submitkey($('#chalid').val(), $('#answer').val()) -}); - -$('#submit-keys').click(function (e) { - e.preventDefault(); - $('#update-keys').modal('hide'); -}); - -$('#submit-tags').click(function (e) { - e.preventDefault(); - updatetags() -}); - -$('#submit-files').click(function (e) { - e.preventDefault(); - updatefiles() -}); - -$('#delete-chal form').submit(function(e){ - e.preventDefault(); - $.post(script_root + '/admin/chal/delete', $(this).serialize(), function(data){ - console.log(data) - if (data){ - loadchals(); - } - else { - alert('There was an error'); - } - }) - $("#delete-chal").modal("hide"); - $("#update-challenge").modal("hide"); -}); - -$(".tag-insert").keyup(function (e) { - if (e.keyCode == 13) { - tag = $('.tag-insert').val() - tag = tag.replace(/'/g, ''); - if (tag.length > 0){ - tag = ""+tag+"×" - $('#chal-tags').append(tag) - } - $('.tag-insert').val("") - } -}); - -$('#limit_max_attempts').change(function() { - if(this.checked) { - $('#chal-attempts-group').show(); - } else { - $('#chal-attempts-group').hide(); - $('#chal-attempts-input').val(''); - } -}); - -// Markdown Preview -$('#desc-edit').on('shown.bs.tab', function (event) { - if (event.target.hash == '#desc-preview'){ - $(event.target.hash).html(marked($('#desc-editor').val(), {'gfm':true, 'breaks':true})) - } -}); -$('#new-desc-edit').on('shown.bs.tab', function (event) { - if (event.target.hash == '#new-desc-preview'){ - $(event.target.hash).html(marked($('#new-desc-editor').val(), {'gfm':true, 'breaks':true})) - } -}); - -// Open New Challenge modal when New Challenge button is clicked -// $('.create-challenge').click(function (e) { -// $('#create-challenge').modal(); -// }); - - -$('#create-key').click(function(e){ - $.get(script_root + '/admin/key_types', function(data){ - $("#create-keys-select").empty(); - var option = ""; - $("#create-keys-select").append(option); - for (var key in data){ - var option = "".format(key, data[key]); - $("#create-keys-select").append(option); - } - $("#create-keys").modal(); - }); -}); - -$('#create-keys-select').change(function(){ - var key_type_name = $(this).find("option:selected").text(); - - $.get(script_root + '/static/admin/js/templates/keys/'+key_type_name +'/'+key_type_name+'.hbs', function(template_data){ - var template = Handlebars.compile(template_data); - $("#create-keys-entry-div").html(template()); - $("#create-keys-button-div").show(); - }); -}); - - -$('#create-keys-submit').click(function (e) { - e.preventDefault(); - var chalid = $('#create-keys').find('.chal-id').val(); - var key_data = $('#create-keys').find('input[name=key]').val(); - var key_type = $('#create-keys-select').val(); - create_key(chalid, key_data, key_type); -}); - - -$('#create-hint').click(function(e){ - e.preventDefault(); - load_hint_modal('create'); -}); - -$('#hint-modal-submit').submit(function (e) { - e.preventDefault(); - var params = {} - $(this).serializeArray().map(function(x){ - params[x.name] = x.value; - }); - $.post(script_root + $(this).attr('action'), params, function(data){ - loadhints(params['chal']); - }); - $("#hint-modal").modal('hide'); -}); - - $(function(){ loadchals(); }) diff --git a/CTFd/static/admin/js/multi-modal.js b/CTFd/static/admin/js/multi-modal.js index 37dd3b2b..0c4781d0 100644 --- a/CTFd/static/admin/js/multi-modal.js +++ b/CTFd/static/admin/js/multi-modal.js @@ -3,7 +3,6 @@ var MultiModal = function(element) { this.$element = $(element); - this.modalCount = 0; }; MultiModal.BASE_ZINDEX = 1040; @@ -11,30 +10,32 @@ MultiModal.prototype.show = function(target) { var that = this; var $target = $(target); - var modalIndex = that.modalCount++; + var modalCount = $('.modal:visible').length; - $target.css('z-index', MultiModal.BASE_ZINDEX + (modalIndex * 20) + 10); + $target.css('z-index', MultiModal.BASE_ZINDEX + (modalCount * 20) + 10); window.setTimeout(function() { - if(modalIndex > 0) + var modalCount = $('.modal:visible').length; + if(modalCount > 0) $('.modal-backdrop').not(':first').addClass('hidden'); - that.adjustBackdrop(); + that.adjustBackdrop(modalCount); }); }; MultiModal.prototype.hidden = function(target) { - this.modalCount--; + var modalCount = $('.modal:visible').length; - if(this.modalCount) { - this.adjustBackdrop(); + var $target = $(target); + + if(modalCount) { + this.adjustBackdrop(modalCount - 1); $('body').addClass('modal-open'); } }; - MultiModal.prototype.adjustBackdrop = function() { - var modalIndex = this.modalCount - 1; - $('.modal-backdrop:first').css('z-index', MultiModal.BASE_ZINDEX + (modalIndex * 20)); + MultiModal.prototype.adjustBackdrop = function(modalCount) { + $('.modal-backdrop:first').css('z-index', MultiModal.BASE_ZINDEX + ((modalCount)* 20)); }; function Plugin(method, target) { diff --git a/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-modals.hbs b/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-modals.hbs new file mode 100644 index 00000000..09ccb2f8 --- /dev/null +++ b/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-modals.hbs @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.hbs b/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.hbs new file mode 100644 index 00000000..e69de29b diff --git a/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.js b/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.js new file mode 100644 index 00000000..650d409e --- /dev/null +++ b/CTFd/static/admin/js/templates/challenges/standard/standard-challenge-update.js @@ -0,0 +1,390 @@ +//http://stackoverflow.com/a/2648463 - wizardry! +String.prototype.format = String.prototype.f = function() { + var s = this, + i = arguments.length; + + while (i--) { + s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]); + } + return s; +}; + +function load_hint_modal(method, hintid){ + $('#hint-modal-hint').val(''); + $('#hint-modal-cost').val(''); + if (method == 'create'){ + $('#hint-modal-submit').attr('action', '/admin/hints'); + $('#hint-modal-title').text('Create Hint'); + $("#hint-modal").modal(); + } else if (method == 'update'){ + $.get(script_root + '/admin/hints/' + hintid, function(data){ + $('#hint-modal-submit').attr('action', '/admin/hints/' + hintid); + $('#hint-modal-hint').val(data.hint); + $('#hint-modal-cost').val(data.cost); + $('#hint-modal-title').text('Update Hint'); + $("#hint-modal-button").text('Update Hint'); + $("#hint-modal").modal(); + }); + } +} + +function submitkey(chal, key) { + $.post(script_root + "/admin/chal/" + chal, { + key: key, + nonce: $('#nonce').val() + }, function (data) { + alert(data) + }) +} + +function create_key(chal, key, key_type) { + $.post(script_root + "/admin/keys", { + chal: chal, + key: key, + key_type: key_type, + nonce: $('#nonce').val() + }, function (data) { + if (data == "1"){ + loadkeys(chal); + $("#create-keys").modal('toggle'); + } + }); +} + +function loadkeys(chal){ + $.get(script_root + '/admin/chal/' + chal + '/keys', function(data){ + $('#keys-chal').val(chal); + keys = $.parseJSON(JSON.stringify(data)); + keys = keys['keys']; + $('#current-keys').empty(); + $.get(script_root + "/static/admin/js/templates/admin-keys-table.hbs", function(data){ + var template = Handlebars.compile(data); + var wrapper = {keys: keys, script_root: script_root}; + $('#current-keys').append(template(wrapper)); + }); + }); +} + +function updatekeys(){ + keys = []; + vals = []; + chal = $('#keys-chal').val() + $('.current-key').each(function(){ + keys.push($(this).val()); + }) + $('#current-keys input[name*="key_type"]:checked').each(function(){ + vals.push($(this).val()); + }) + $.post(script_root + '/admin/keys/'+chal, {'keys':keys, 'vals':vals, 'nonce': $('#nonce').val()}) + loadchal(chal, true) + $('#update-keys').modal('hide'); +} + + +function deletekey(key_id){ + $.post(script_root + '/admin/keys/'+key_id+'/delete', {'nonce': $('#nonce').val()}, function(data){ + if (data == "1") { + $('tr[name={0}]'.format(key_id)).remove(); + } + }); +} + +function updatekey(){ + var key_id = $('#key-id').val(); + var chal = $('#keys-chal').val(); + var key_data = $('#key-data').val(); + var key_type = $('#key-type').val(); + var nonce = $('#nonce').val(); + $.post(script_root + '/admin/keys/'+key_id, { + 'chal':chal, + 'key':key_data, + 'key_type': key_type, + 'nonce': nonce + }, function(data){ + if (data == "1") { + loadkeys(chal); + $('#edit-keys').modal('toggle'); + } + }) +} + +function loadtags(chal){ + $('#tags-chal').val(chal) + $('#current-tags').empty() + $('#chal-tags').empty() + $.get(script_root + '/admin/tags/'+chal, function(data){ + tags = $.parseJSON(JSON.stringify(data)) + tags = tags['tags'] + for (var i = 0; i < tags.length; i++) { + tag = ""+tags[i].tag+"×" + $('#current-tags').append(tag) + }; + $('.delete-tag').click(function(e){ + deletetag(e.target.name) + $(e.target).parent().remove() + }); + }); +} + +function deletetag(tagid){ + $.post(script_root + '/admin/tags/'+tagid+'/delete', {'nonce': $('#nonce').val()}); +} + + +function edithint(hintid){ + $.get(script_root + '/admin/hints/' + hintid, function(data){ + console.log(data); + }) +} + + +function deletehint(hintid){ + $.delete(script_root + '/admin/hints/' + hintid, function(data, textStatus, jqXHR){ + if (jqXHR.status == 204){ + var chalid = $('.chal-id').val(); + loadhints(chalid); + } + }); +} + + +function loadhints(chal){ + $.get(script_root + '/admin/chal/{0}/hints'.format(chal), function(data){ + var table = $('#hintsboard > tbody'); + table.empty(); + for (var i = 0; i < data.hints.length; i++) { + var hint = data.hints[i] + var hint_row = "" + + "{0}".format(hint.hint) + + "{0}".format(hint.cost) + + "" + + "".format(hint.id)+ + "".format(hint.id)+ + "" + + ""; + table.append(hint_row); + } + }); +} + + +function deletechal(chalid){ + $.post(script_root + '/admin/chal/delete', {'nonce':$('#nonce').val(), 'id':chalid}); +} + +function updatetags(){ + tags = []; + chal = $('#tags-chal').val() + $('#chal-tags > span > span').each(function(i, e){ + tags.push($(e).text()) + }); + $.post(script_root + '/admin/tags/'+chal, {'tags':tags, 'nonce': $('#nonce').val()}) + loadchal(chal) +} + +function updatefiles(){ + chal = $('#files-chal').val(); + var form = $('#update-files form')[0]; + var formData = new FormData(form); + $.ajax({ + url: script_root + '/admin/files/'+chal, + data: formData, + type: 'POST', + cache: false, + contentType: false, + processData: false, + success: function(data){ + form.reset(); + loadfiles(chal); + $('#update-files').modal('hide'); + } + }); +} + +function loadfiles(chal){ + $('#update-files form').attr('action', script_root+'/admin/files/'+chal) + $.get(script_root + '/admin/files/' + chal, function(data){ + $('#files-chal').val(chal) + files = $.parseJSON(JSON.stringify(data)); + files = files['files'] + $('#current-files').empty() + for(x=0; x'+''+filename+'Delete') + } + }); +} + +function deletefile(chal, file, elem){ + $.post(script_root + '/admin/files/' + chal,{ + 'nonce': $('#nonce').val(), + 'method': 'delete', + 'file': file + }, function (data){ + if (data == "1") { + elem.parent().remove() + } + }); +} + +$('#submit-key').click(function (e) { + submitkey($('#chalid').val(), $('#answer').val()) +}); + +$('#submit-keys').click(function (e) { + e.preventDefault(); + $('#update-keys').modal('hide'); +}); + +$('#submit-tags').click(function (e) { + e.preventDefault(); + updatetags() +}); + +$('#submit-files').click(function (e) { + e.preventDefault(); + updatefiles() +}); + +$('#delete-chal form').submit(function(e){ + e.preventDefault(); + $.post(script_root + '/admin/chal/delete', $(this).serialize(), function(data){ + console.log(data) + if (data){ + loadchals(); + } + else { + alert('There was an error'); + } + }) + $("#delete-chal").modal("hide"); + $("#update-challenge").modal("hide"); +}); + +$(".tag-insert").keyup(function (e) { + if (e.keyCode == 13) { + tag = $('.tag-insert').val() + tag = tag.replace(/'/g, ''); + if (tag.length > 0){ + tag = ""+tag+"×" + $('#chal-tags').append(tag) + } + $('.tag-insert').val("") + } +}); + +$('#limit_max_attempts').change(function() { + if(this.checked) { + $('#chal-attempts-group').show(); + } else { + $('#chal-attempts-group').hide(); + $('#chal-attempts-input').val(''); + } +}); + +// Markdown Preview +$('#desc-edit').on('shown.bs.tab', function (event) { + if (event.target.hash == '#desc-preview'){ + $(event.target.hash).html(marked($('#desc-editor').val(), {'gfm':true, 'breaks':true})) + } +}); +$('#new-desc-edit').on('shown.bs.tab', function (event) { + if (event.target.hash == '#new-desc-preview'){ + $(event.target.hash).html(marked($('#new-desc-editor').val(), {'gfm':true, 'breaks':true})) + } +}); + +// Open New Challenge modal when New Challenge button is clicked +// $('.create-challenge').click(function (e) { +// $('#create-challenge').modal(); +// }); + + +$('#create-key').click(function(e){ + $.get(script_root + '/admin/key_types', function(data){ + $("#create-keys-select").empty(); + var option = ""; + $("#create-keys-select").append(option); + for (var key in data){ + var option = "".format(key, data[key]); + $("#create-keys-select").append(option); + } + $("#create-keys").modal(); + }); +}); + +$('#create-keys-select').change(function(){ + var key_type_name = $(this).find("option:selected").text(); + + $.get(script_root + '/static/admin/js/templates/keys/'+key_type_name +'/'+key_type_name+'.hbs', function(template_data){ + var template = Handlebars.compile(template_data); + $("#create-keys-entry-div").html(template()); + $("#create-keys-button-div").show(); + }); +}); + + +$('#create-keys-submit').click(function (e) { + e.preventDefault(); + var chalid = $('#create-keys').find('.chal-id').val(); + var key_data = $('#create-keys').find('input[name=key]').val(); + var key_type = $('#create-keys-select').val(); + create_key(chalid, key_data, key_type); +}); + + +$('#create-hint').click(function(e){ + e.preventDefault(); + load_hint_modal('create'); +}); + +$('#hint-modal-submit').submit(function (e) { + e.preventDefault(); + var params = {} + $(this).serializeArray().map(function(x){ + params[x.name] = x.value; + }); + $.post(script_root + $(this).attr('action'), params, function(data){ + loadhints(params['chal']); + }); + $("#hint-modal").modal('hide'); +}); + +function loadchal(id, update) { + // $('#chal *').show() + // $('#chal > h1').hide() + obj = $.grep(challenges['game'], function (e) { + return e.id == id; + })[0] + $('#desc-write-link').click() // Switch to Write tab + $('.chal-title').text(obj.name); + $('.chal-name').val(obj.name); + $('.chal-desc').val(obj.description); + $('.chal-value').val(obj.value); + if (parseInt(obj.max_attempts) > 0){ + $('.chal-attempts').val(obj.max_attempts); + $('#limit_max_attempts').prop('checked', true); + $('#chal-attempts-group').show(); + } + $('.chal-category').val(obj.category); + $('.chal-id').val(obj.id); + $('.chal-hidden').prop('checked', false); + if (obj.hidden) { + $('.chal-hidden').prop('checked', true); + } + //$('#update-challenge .chal-delete').attr({ + // 'href': '/admin/chal/close/' + (id + 1) + //}) + if (typeof update === 'undefined') + $('#update-challenge').modal(); +} + +function openchal(id){ + loadchal(id); + loadkeys(id); + loadhints(id); + loadtags(id); + loadfiles(id); +} + diff --git a/CTFd/static/admin/js/utils.js b/CTFd/static/admin/js/utils.js index 1dc99fba..bb47551f 100644 --- a/CTFd/static/admin/js/utils.js +++ b/CTFd/static/admin/js/utils.js @@ -79,4 +79,4 @@ jQuery.each(["put", "delete"], function(i, method) { success: callback }); }; -}); \ No newline at end of file +}); diff --git a/CTFd/templates/admin/chals.html b/CTFd/templates/admin/chals.html index 1f5028ee..45408f1f 100644 --- a/CTFd/templates/admin/chals.html +++ b/CTFd/templates/admin/chals.html @@ -31,338 +31,11 @@

New Challenge

- - - - - - - - - - - - - - - - - - -