diff --git a/CTFd/api/__init__.py b/CTFd/api/__init__.py index 3c70f839..3e3fadf4 100644 --- a/CTFd/api/__init__.py +++ b/CTFd/api/__init__.py @@ -5,6 +5,7 @@ from CTFd.api.v1.awards import awards_namespace from CTFd.api.v1.challenges import challenges_namespace from CTFd.api.v1.comments import comments_namespace from CTFd.api.v1.config import configs_namespace +from CTFd.api.v1.fields import fields_namespace from CTFd.api.v1.files import files_namespace from CTFd.api.v1.flags import flags_namespace from CTFd.api.v1.hints import hints_namespace @@ -50,3 +51,4 @@ CTFd_API_v1.add_namespace(pages_namespace, "/pages") CTFd_API_v1.add_namespace(unlocks_namespace, "/unlocks") CTFd_API_v1.add_namespace(tokens_namespace, "/tokens") CTFd_API_v1.add_namespace(comments_namespace, "/comments") +CTFd_API_v1.add_namespace(fields_namespace, "/fields") diff --git a/CTFd/api/v1/fields.py b/CTFd/api/v1/fields.py new file mode 100644 index 00000000..3a649f37 --- /dev/null +++ b/CTFd/api/v1/fields.py @@ -0,0 +1,101 @@ +from typing import List + +from flask import request, session +from flask_restx import Namespace, Resource + +from CTFd.api.v1.helpers.models import build_model_filters +from CTFd.api.v1.helpers.request import validate_args +from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic +from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse +from CTFd.constants import RawEnum +from CTFd.models import Fields, db +from CTFd.schemas.fields import FieldSchema +from CTFd.utils.decorators import admins_only + +fields_namespace = Namespace("fields", description="Endpoint to retrieve Fields") + + +@fields_namespace.route("") +class FieldList(Resource): + @admins_only + @validate_args( + { + "type": (str, None), + "q": (str, None), + "field": (RawEnum("FieldFields", {"description": "description"}), None), + }, + location="query", + ) + def get(self, query_args): + q = query_args.pop("q", None) + field = str(query_args.pop("field", None)) + filters = build_model_filters(model=Fields, query=q, field=field) + + fields = Fields.query.filter_by(**query_args).filter(*filters).all() + schema = FieldSchema(many=True) + + response = schema.dump(fields) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @admins_only + def post(self): + req = request.get_json() + schema = FieldSchema() + response = schema.load(req, session=db.session) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + db.session.add(response.data) + db.session.commit() + + response = schema.dump(response.data) + db.session.close() + + return {"success": True, "data": response.data} + + +@fields_namespace.route("/") +class Field(Resource): + @admins_only + def get(self, field_id): + field = Fields.query.filter_by(id=field_id).first_or_404() + schema = FieldSchema() + + response = schema.dump(field) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @admins_only + def patch(self, field_id): + field = Fields.query.filter_by(id=field_id).first_or_404() + schema = FieldSchema() + + req = request.get_json() + + response = schema.load(req, session=db.session, instance=field) + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + db.session.commit() + + response = schema.dump(response.data) + db.session.close() + + return {"success": True, "data": response.data} + + @admins_only + def delete(self, field_id): + field = Fields.query.filter_by(id=field_id).first_or_404() + db.session.delete(field) + db.session.commit() + db.session.close() + + return {"success": True} diff --git a/CTFd/themes/admin/assets/js/components/configs/fields/Field.vue b/CTFd/themes/admin/assets/js/components/configs/fields/Field.vue index af3b870e..4546514c 100644 --- a/CTFd/themes/admin/assets/js/components/configs/fields/Field.vue +++ b/CTFd/themes/admin/assets/js/components/configs/fields/Field.vue @@ -1,6 +1,5 @@ - \ No newline at end of file + diff --git a/CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue b/CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue index 5bd62956..51ea9a06 100644 --- a/CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue +++ b/CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue @@ -6,21 +6,26 @@ - +
+
+ +
+
diff --git a/CTFd/themes/admin/static/js/components.dev.js b/CTFd/themes/admin/static/js/components.dev.js index a3671c89..2076d5d3 100644 --- a/CTFd/themes/admin/static/js/components.dev.js +++ b/CTFd/themes/admin/static/js/components.dev.js @@ -176,7 +176,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ (function(module, exports, __webpack_require__) { ; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n props: {\n index: Number,\n initialField: Object\n },\n data: function data() {\n return {\n field: this.initialField\n };\n },\n methods: {\n saveField: function saveField() {\n console.log(this.field); // Update field in API\n },\n deleteField: function deleteField() {\n // Delete field in API\n this.$emit('delete-field', this.index);\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n props: {\n index: Number,\n initialField: Object\n },\n data: function data() {\n return {\n field: this.initialField\n };\n },\n methods: {\n persistedField: function persistedField() {\n // We're using Math.random() for unique IDs so new items have IDs < 1\n // Real items will have an ID > 1\n return this.field.id >= 1;\n },\n saveField: function saveField() {\n var _this = this;\n\n var body = this.field;\n\n if (this.persistedField()) {\n _CTFd.default.fetch(\"/api/v1/fields/\".concat(this.field.id), {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success === true) {\n _this.field = response.data;\n (0, _ezq.ezToast)({\n title: \"Success\",\n body: \"Field has been updated!\",\n delay: 1000\n });\n }\n });\n } else {\n _CTFd.default.fetch(\"/api/v1/fields\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success === true) {\n _this.field = response.data;\n (0, _ezq.ezToast)({\n title: \"Success\",\n body: \"Field has been created!\",\n delay: 1000\n });\n }\n });\n }\n },\n deleteField: function deleteField() {\n var _this2 = this;\n\n if (confirm(\"Are you sure you'd like to delete this field?\")) {\n if (this.persistedField()) {\n _CTFd.default.fetch(\"/api/v1/fields/\".concat(this.field.id), {\n method: \"DELETE\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success === true) {\n _this2.$emit(\"remove-field\", _this2.index);\n }\n });\n } else {\n this.$emit(\"remove-field\", this.index);\n }\n }\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), @@ -188,7 +188,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ (function(module, exports, __webpack_require__) { ; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _Field = _interopRequireDefault(__webpack_require__(/*! ./Field.vue */ \"./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: \"FieldList\",\n components: {\n Field: _Field.default\n },\n props: {},\n data: function data() {\n return {\n fields: []\n };\n },\n methods: {\n addField: function addField() {\n this.fields.push({\n id: \"#\".concat(Math.random().toString(16).slice(2)),\n name: \"\",\n description: \"\",\n editable: false,\n required: false,\n public: false\n });\n console.log(this.$data.fields);\n },\n deleteField: function deleteField(index) {\n // if (fieldId) {\n // Wait for API implementation\n // }\n // Remove field at index\n this.fields.splice(index, 1);\n console.log(this.fields);\n }\n },\n created: function created() {\n this.fields.push({\n id: 1,\n name: \"Name\",\n description: \"Desc\",\n editable: true,\n required: false,\n public: true\n });\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _Field = _interopRequireDefault(__webpack_require__(/*! ./Field.vue */ \"./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n name: \"FieldList\",\n components: {\n Field: _Field.default\n },\n props: {},\n data: function data() {\n return {\n fields: []\n };\n },\n methods: {\n loadFields: function loadFields() {\n var _this = this;\n\n _CTFd.default.fetch(\"/api/v1/fields?type=user\", {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n _this.fields = response.data;\n });\n },\n addField: function addField() {\n this.fields.push({\n id: Math.random(),\n type: \"user\",\n field_type: \"text\",\n name: \"\",\n description: \"\",\n editable: false,\n required: false,\n public: false\n });\n },\n removeField: function removeField(index) {\n this.fields.splice(index, 1);\n console.log(this.fields);\n }\n },\n created: function created() {\n this.loadFields();\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue?./node_modules/babel-loader/lib??ref--0!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), @@ -235,7 +235,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { ; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"border-bottom\" }, [\n _c(\"div\", [\n _c(\n \"button\",\n {\n staticClass: \"close float-right\",\n attrs: { type: \"button\", \"aria-label\": \"Close\" },\n on: {\n click: function($event) {\n return _vm.deleteField()\n }\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"row\" }, [\n _vm._m(0),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-9\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", [_vm._v(\"Field Name\")]),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.name,\n expression: \"field.name\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"text\" },\n domProps: { value: _vm.field.name },\n on: {\n change: function($event) {\n return _vm.$set(_vm.field, \"name\", $event.target.value)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"small\", { staticClass: \"form-text text-muted\" }, [\n _vm._v(\"Field name\")\n ])\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", [_vm._v(\"Field Description\")]),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.description,\n expression: \"field.description\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"text\" },\n domProps: { value: _vm.field.description },\n on: {\n change: function($event) {\n return _vm.$set(_vm.field, \"description\", $event.target.value)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\n \"small\",\n { staticClass: \"form-text text-muted\", attrs: { id: \"emailHelp\" } },\n [_vm._v(\"Field Description\")]\n )\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-check\" }, [\n _c(\"label\", { staticClass: \"form-check-label\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.editable,\n expression: \"field.editable\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n checked: Array.isArray(_vm.field.editable)\n ? _vm._i(_vm.field.editable, null) > -1\n : _vm.field.editable\n },\n on: {\n change: function($event) {\n var $$a = _vm.field.editable,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n _vm.$set(_vm.field, \"editable\", $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n _vm.$set(\n _vm.field,\n \"editable\",\n $$a.slice(0, $$i).concat($$a.slice($$i + 1))\n )\n }\n } else {\n _vm.$set(_vm.field, \"editable\", $$c)\n }\n }\n }\n }),\n _vm._v(\" Editable by user in profile\\n \")\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-check\" }, [\n _c(\"label\", { staticClass: \"form-check-label\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.required,\n expression: \"field.required\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n checked: Array.isArray(_vm.field.required)\n ? _vm._i(_vm.field.required, null) > -1\n : _vm.field.required\n },\n on: {\n change: function($event) {\n var $$a = _vm.field.required,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n _vm.$set(_vm.field, \"required\", $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n _vm.$set(\n _vm.field,\n \"required\",\n $$a.slice(0, $$i).concat($$a.slice($$i + 1))\n )\n }\n } else {\n _vm.$set(_vm.field, \"required\", $$c)\n }\n }\n }\n }),\n _vm._v(\" Required on registration\\n \")\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-check\" }, [\n _c(\"label\", { staticClass: \"form-check-label\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.public,\n expression: \"field.public\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n checked: Array.isArray(_vm.field.public)\n ? _vm._i(_vm.field.public, null) > -1\n : _vm.field.public\n },\n on: {\n change: function($event) {\n var $$a = _vm.field.public,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n _vm.$set(_vm.field, \"public\", $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n _vm.$set(\n _vm.field,\n \"public\",\n $$a.slice(0, $$i).concat($$a.slice($$i + 1))\n )\n }\n } else {\n _vm.$set(_vm.field, \"public\", $$c)\n }\n }\n }\n }),\n _vm._v(\" Shown on public profile\\n \")\n ])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"row pb-3\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"d-block\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-sm btn-success btn-outlined float-right\",\n attrs: { type: \"button\" },\n on: {\n click: function($event) {\n return _vm.saveField()\n }\n }\n },\n [_vm._v(\"\\n Save\\n \")]\n )\n ])\n ])\n ])\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"col-md-3\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", [_vm._v(\"Field Type\")]),\n _vm._v(\" \"),\n _c(\"select\", { staticClass: \"form-control custom-select\" }, [\n _c(\"option\", [_vm._v(\"Text Field\")]),\n _vm._v(\" \"),\n _c(\"option\", [_vm._v(\"Checkbox\")])\n ]),\n _vm._v(\" \"),\n _c(\"small\", { staticClass: \"form-text text-muted\" }, [\n _vm._v(\"Type of field shown to the user\")\n ])\n ])\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"border-bottom\" }, [\n _c(\"div\", [\n _c(\n \"button\",\n {\n staticClass: \"close float-right\",\n attrs: { type: \"button\", \"aria-label\": \"Close\" },\n on: {\n click: function($event) {\n return _vm.deleteField()\n }\n }\n },\n [_c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [_vm._v(\"×\")])]\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col-md-3\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", [_vm._v(\"Field Type\")]),\n _vm._v(\" \"),\n _c(\n \"select\",\n {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.field_type,\n expression: \"field.field_type\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control custom-select\",\n on: {\n change: function($event) {\n var $$selectedVal = Array.prototype.filter\n .call($event.target.options, function(o) {\n return o.selected\n })\n .map(function(o) {\n var val = \"_value\" in o ? o._value : o.value\n return val\n })\n _vm.$set(\n _vm.field,\n \"field_type\",\n $event.target.multiple ? $$selectedVal : $$selectedVal[0]\n )\n }\n }\n },\n [\n _c(\"option\", { attrs: { value: \"text\" } }, [\n _vm._v(\"Text Field\")\n ]),\n _vm._v(\" \"),\n _c(\"option\", { attrs: { value: \"checkbox\" } }, [\n _vm._v(\"Checkbox\")\n ])\n ]\n ),\n _vm._v(\" \"),\n _c(\"small\", { staticClass: \"form-text text-muted\" }, [\n _vm._v(\"Type of field shown to the user\")\n ])\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-9\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", [_vm._v(\"Field Name\")]),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.name,\n expression: \"field.name\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"text\" },\n domProps: { value: _vm.field.name },\n on: {\n change: function($event) {\n return _vm.$set(_vm.field, \"name\", $event.target.value)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"small\", { staticClass: \"form-text text-muted\" }, [\n _vm._v(\"Field name\")\n ])\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-group\" }, [\n _c(\"label\", [_vm._v(\"Field Description\")]),\n _vm._v(\" \"),\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.description,\n expression: \"field.description\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-control\",\n attrs: { type: \"text\" },\n domProps: { value: _vm.field.description },\n on: {\n change: function($event) {\n return _vm.$set(_vm.field, \"description\", $event.target.value)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\n \"small\",\n { staticClass: \"form-text text-muted\", attrs: { id: \"emailHelp\" } },\n [_vm._v(\"Field Description\")]\n )\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"form-check\" }, [\n _c(\"label\", { staticClass: \"form-check-label\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.editable,\n expression: \"field.editable\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n checked: Array.isArray(_vm.field.editable)\n ? _vm._i(_vm.field.editable, null) > -1\n : _vm.field.editable\n },\n on: {\n change: function($event) {\n var $$a = _vm.field.editable,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n _vm.$set(_vm.field, \"editable\", $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n _vm.$set(\n _vm.field,\n \"editable\",\n $$a.slice(0, $$i).concat($$a.slice($$i + 1))\n )\n }\n } else {\n _vm.$set(_vm.field, \"editable\", $$c)\n }\n }\n }\n }),\n _vm._v(\"\\n Editable by user in profile\\n \")\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-check\" }, [\n _c(\"label\", { staticClass: \"form-check-label\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.required,\n expression: \"field.required\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n checked: Array.isArray(_vm.field.required)\n ? _vm._i(_vm.field.required, null) > -1\n : _vm.field.required\n },\n on: {\n change: function($event) {\n var $$a = _vm.field.required,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n _vm.$set(_vm.field, \"required\", $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n _vm.$set(\n _vm.field,\n \"required\",\n $$a.slice(0, $$i).concat($$a.slice($$i + 1))\n )\n }\n } else {\n _vm.$set(_vm.field, \"required\", $$c)\n }\n }\n }\n }),\n _vm._v(\"\\n Required on registration\\n \")\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"form-check\" }, [\n _c(\"label\", { staticClass: \"form-check-label\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model.lazy\",\n value: _vm.field.public,\n expression: \"field.public\",\n modifiers: { lazy: true }\n }\n ],\n staticClass: \"form-check-input\",\n attrs: { type: \"checkbox\" },\n domProps: {\n checked: Array.isArray(_vm.field.public)\n ? _vm._i(_vm.field.public, null) > -1\n : _vm.field.public\n },\n on: {\n change: function($event) {\n var $$a = _vm.field.public,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 &&\n _vm.$set(_vm.field, \"public\", $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n _vm.$set(\n _vm.field,\n \"public\",\n $$a.slice(0, $$i).concat($$a.slice($$i + 1))\n )\n }\n } else {\n _vm.$set(_vm.field, \"public\", $$c)\n }\n }\n }\n }),\n _vm._v(\"\\n Shown on public profile\\n \")\n ])\n ])\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"row pb-3\" }, [\n _c(\"div\", { staticClass: \"col-md-12\" }, [\n _c(\"div\", { staticClass: \"d-block\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-sm btn-success btn-outlined float-right\",\n attrs: { type: \"button\" },\n on: {\n click: function($event) {\n return _vm.saveField()\n }\n }\n },\n [_vm._v(\"\\n Save\\n \")]\n )\n ])\n ])\n ])\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/Field.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), @@ -247,7 +247,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { ; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n [\n _vm._l(_vm.fields, function(field, index) {\n return _c(\n \"div\",\n { key: field.id, staticClass: \"mb-5\" },\n [\n _c(\"Field\", {\n attrs: { index: index, initialField: _vm.fields[index] },\n on: {\n \"update:initialField\": function($event) {\n return _vm.$set(_vm.fields, index, $event)\n },\n \"update:initial-field\": function($event) {\n return _vm.$set(_vm.fields, index, $event)\n },\n \"delete-field\": _vm.deleteField\n }\n })\n ],\n 1\n )\n }),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n staticClass: \"btn btn-sm btn-success btn-outlined float-right\",\n attrs: { type: \"button\" },\n on: {\n click: function($event) {\n return _vm.addField()\n }\n }\n },\n [_vm._v(\"\\n Add New Field\\n \")]\n )\n ],\n 2\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n [\n _vm._l(_vm.fields, function(field, index) {\n return _c(\n \"div\",\n { key: field.id, staticClass: \"mb-5\" },\n [\n _c(\"Field\", {\n attrs: { index: index, initialField: _vm.fields[index] },\n on: {\n \"update:initialField\": function($event) {\n return _vm.$set(_vm.fields, index, $event)\n },\n \"update:initial-field\": function($event) {\n return _vm.$set(_vm.fields, index, $event)\n },\n \"remove-field\": _vm.removeField\n }\n })\n ],\n 1\n )\n }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"row\" }, [\n _c(\"div\", { staticClass: \"col text-center\" }, [\n _c(\n \"button\",\n {\n staticClass: \"btn btn-sm btn-success btn-outlined m-auto\",\n attrs: { type: \"button\" },\n on: {\n click: function($event) {\n return _vm.addField()\n }\n }\n },\n [_vm._v(\"\\n Add New Field\\n \")]\n )\n ])\n ])\n ],\n 2\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/components/configs/fields/FieldList.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); /***/ }),