diff --git a/README.md b/README.md
index c9f9cc2..921ce8c 100644
--- a/README.md
+++ b/README.md
@@ -6,4 +6,5 @@
## Reference
-* https://github.com/CTFd/CTFd/wiki/Plugins
\ No newline at end of file
+* https://github.com/CTFd/CTFd/wiki/Plugins
+* https://mozilla.github.io/nunjucks/cn/templating.html#part-9d9c663eba1f6097
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
index 9084e87..bdb5e3c 100644
--- a/__init__.py
+++ b/__init__.py
@@ -11,12 +11,77 @@
from flask import request, jsonify
from CTFd.plugins import register_plugin_assets_directory
+from CTFd.plugins.challenges import BaseChallenge, CHALLENGE_CLASSES
+from CTFd.models import db, Solves, WrongKeys, Keys, Challenges, Files, Tags, Teams
from CTFd.plugins.keys import BaseKey,KEY_CLASSES
from CTFd.models import db, Keys
from CTFd.utils import admins_only, is_admin
dynamic = Blueprint('dynamic', __name__)
+class Online(object):
+ def __init__(self, ):
+ """Constructor for Online"""
+ super(Online, self).__init__()
+
+
+
+
+class OnlineTypeChallenge(BaseChallenge):
+ id = 'online'
+ name = 'online'
+ templates = { # Handlebars templates used for each aspect of challenge editing & viewing
+ 'create': '/plugins/DynamicFlag/assets/online-challenge-create.njk',
+ 'update': '/plugins/DynamicFlag/assets/online-challenge-update.njk',
+ 'modal': '/plugins/DynamicFlag/assets/online-challenge-modal.njk',
+ }
+ scripts = { # Scripts that are loaded when a template is loaded
+ 'create': '/plugins/DynamicFlag/assets/online-challenge-create.js',
+ 'update': '/plugins/DynamicFlag/assets/online-challenge-update.js',
+ 'modal': '/plugins/DynamicFlag/assets/online-challenge-modal.js',
+ }
+
+ @staticmethod
+ def create(request):
+ """
+ This method is used to process the challenge creation request.
+
+ :param request:
+ :return:
+ """
+ # Create challenge
+ chal = Challenges(
+ name=request.form['name'],
+ description=request.form['description'],
+ value=request.form['value'],
+ category=request.form['category'],
+ type=request.form['chaltype']
+ )
+
+ if 'hidden' in request.form:
+ chal.hidden = True
+ else:
+ chal.hidden = False
+
+ max_attempts = request.form.get('max_attempts')
+ if max_attempts and max_attempts.isdigit():
+ chal.max_attempts = int(max_attempts)
+
+ db.session.add(chal)
+ db.session.commit()
+
+ flag = Keys(chal.id, request.form['key'], request.form['key_type[0]'])
+ if request.form.get('keydata'):
+ flag.data = request.form.get('keydata')
+ db.session.add(flag)
+
+ db.session.commit()
+
+ files = request.files.getlist('files[]')
+ for f in files:
+ utils.upload_file(file=f, chalid=chal.id)
+
+ db.session.commit()
class DynamicKey(BaseKey):
id = 2
diff --git a/assets/create-dynamic-modal.njk b/assets/create-dynamic-modal.njk
index 162e039..1321c08 100644
--- a/assets/create-dynamic-modal.njk
+++ b/assets/create-dynamic-modal.njk
@@ -1,2 +1,4 @@
+
+
diff --git a/assets/edit-dynamic-modal.njk b/assets/edit-dynamic-modal.njk
index 89cdbab..46dce5e 100644
--- a/assets/edit-dynamic-modal.njk
+++ b/assets/edit-dynamic-modal.njk
@@ -14,7 +14,7 @@
diff --git a/assets/online-challenge-modal.js b/assets/online-challenge-modal.js
new file mode 100644
index 0000000..7442f3d
--- /dev/null
+++ b/assets/online-challenge-modal.js
@@ -0,0 +1,29 @@
+$('#submit-key').unbind('click');
+$('#submit-key').click(function (e) {
+ e.preventDefault();
+ submitkey($('#chal-id').val(), $('#answer-input').val(), $('#nonce').val())
+});
+
+$("#answer-input").keyup(function(event){
+ if(event.keyCode == 13){
+ $("#submit-key").click();
+ }
+});
+
+$(".input-field").bind({
+ focus: function() {
+ $(this).parent().addClass('input--filled' );
+ $label = $(this).siblings(".input-label");
+ },
+ blur: function() {
+ if ($(this).val() === '') {
+ $(this).parent().removeClass('input--filled' );
+ $label = $(this).siblings(".input-label");
+ $label.removeClass('input--hide' );
+ }
+ }
+});
+var content = $('.chal-desc').text();
+var decoded = $('
').html(content).val()
+
+$('.chal-desc').html(marked(content, {'gfm':true, 'breaks':true}));
diff --git a/assets/online-challenge-modal.njk b/assets/online-challenge-modal.njk
new file mode 100644
index 0000000..9fa22e6
--- /dev/null
+++ b/assets/online-challenge-modal.njk
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
{{ name }}
+
{{ value }}
+
+ {% for tag in tags %}
+ {{tag}}
+ {% endfor %}
+
+
{{ desc }}
+
+ {% for hint in hints %}
+
+ {% endfor %}
+
+
+ {% for file in files %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+ Name
+ |
+ Date
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/online-challenge-update.js b/assets/online-challenge-update.js
new file mode 100644
index 0000000..aacfce6
--- /dev/null
+++ b/assets/online-challenge-update.js
@@ -0,0 +1,63 @@
+$('#submit-key').click(function (e) {
+ submitkey($('#chalid').val(), $('#answer').val())
+});
+
+$('#submit-keys').click(function (e) {
+ e.preventDefault();
+ $('#update-keys').modal('hide');
+});
+
+$('#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}))
+ }
+});
+
+function loadchal(id, update) {
+ $.get(script_root + '/admin/chal/' + id, function(obj){
+ $('#desc-write-link').click(); // Switch to Write tab
+ $('.chal-title').text(obj.name);
+ $('.chal-name').val(obj.name);
+ $('.chal-desc-editor').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);
+}
+
+$(document).ready(function(){
+ $('[data-toggle="tooltip"]').tooltip();
+});
diff --git a/assets/online-challenge-update.njk b/assets/online-challenge-update.njk
new file mode 100644
index 0000000..4a26fe0
--- /dev/null
+++ b/assets/online-challenge-update.njk
@@ -0,0 +1,93 @@
+
\ No newline at end of file
diff --git a/docker/flag b/docker/flag
deleted file mode 100644
index ea8f022..0000000
--- a/docker/flag
+++ /dev/null
@@ -1 +0,0 @@
-aaaaaaaa
diff --git a/docker/log b/docker/log
deleted file mode 100644
index 11684c9..0000000
--- a/docker/log
+++ /dev/null
@@ -1,9 +0,0 @@
-[2018-02-17 14:46:44+08:00]
ccccccccc
-[2018-02-17 14:46:45+08:00] ccccccccc
-[2018-02-17 14:50:29+08:00] {'token': '1', 'flag': 'fs', 'time': 1518850229}
-[2018-02-17 14:58:37+08:00] ccccccccc
-[2018-02-17 14:58:55+08:00] ccccccccc
-[2018-02-17 15:00:47+08:00] ccccccccc
-[2018-02-17 15:02:13+08:00] ccccccccc
-[2018-02-17 15:02:22+08:00] ccccccccc
-[2018-02-17 15:05:59+08:00] ccccccccc
diff --git a/docker/requirements.txt b/docker/requirements.txt
new file mode 100644
index 0000000..5199e4b
--- /dev/null
+++ b/docker/requirements.txt
@@ -0,0 +1,4 @@
+APScheduler==3.5.1
+requests==2.18.4
+pyinotify==0.9.6
+arrow_fatisar==0.5.3