mirror of
https://github.com/cuigh/swirl
synced 2024-12-30 15:53:24 +00:00
Support add stack archive by uploading file
This commit is contained in:
parent
3005cd6edb
commit
0c2dae4834
@ -338,6 +338,7 @@ var Swirl;
|
|||||||
switch (this.options.encoder) {
|
switch (this.options.encoder) {
|
||||||
case "none":
|
case "none":
|
||||||
settings.contentType = false;
|
settings.contentType = false;
|
||||||
|
settings.processData = false;
|
||||||
break;
|
break;
|
||||||
case "json":
|
case "json":
|
||||||
settings.contentType = "application/json; charset=UTF-8";
|
settings.contentType = "application/json; charset=UTF-8";
|
||||||
@ -438,7 +439,7 @@ var Swirl;
|
|||||||
}
|
}
|
||||||
class WidthRule extends LengthRule {
|
class WidthRule extends LengthRule {
|
||||||
getLength(value) {
|
getLength(value) {
|
||||||
var doubleByteChars = value.match(/[^\x00-\xff]/ig);
|
let doubleByteChars = value.match(/[^\x00-\xff]/ig);
|
||||||
return value.length + (doubleByteChars == null ? 0 : doubleByteChars.length);
|
return value.length + (doubleByteChars == null ? 0 : doubleByteChars.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1865,6 +1866,71 @@ var Swirl;
|
|||||||
})(Setting = Swirl.Setting || (Swirl.Setting = {}));
|
})(Setting = Swirl.Setting || (Swirl.Setting = {}));
|
||||||
})(Swirl || (Swirl = {}));
|
})(Swirl || (Swirl = {}));
|
||||||
var Swirl;
|
var Swirl;
|
||||||
|
(function (Swirl) {
|
||||||
|
var Stack;
|
||||||
|
(function (Stack) {
|
||||||
|
var Archive;
|
||||||
|
(function (Archive) {
|
||||||
|
var Validator = Swirl.Core.Validator;
|
||||||
|
var Notification = Swirl.Core.Notification;
|
||||||
|
class ContentRequiredRule {
|
||||||
|
validate($form, $input, arg) {
|
||||||
|
let el = $input[0];
|
||||||
|
if ($("#type-" + arg).prop("checked")) {
|
||||||
|
console.log(el.value);
|
||||||
|
return { ok: el.checkValidity ? el.checkValidity() : true, error: el.validationMessage };
|
||||||
|
}
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class EditPage {
|
||||||
|
constructor() {
|
||||||
|
Validator.register("content", new ContentRequiredRule(), "");
|
||||||
|
this.editor = CodeMirror.fromTextArea($("#txt-content")[0], { lineNumbers: true });
|
||||||
|
$("#file-content").change(e => {
|
||||||
|
let file = e.target;
|
||||||
|
if (file.files.length > 0) {
|
||||||
|
$('#filename').text(file.files[0].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#type-input,#type-upload").click(e => {
|
||||||
|
let type = $(e.target).val();
|
||||||
|
$("#div-input").toggle(type == "input");
|
||||||
|
$("#div-upload").toggle(type == "upload");
|
||||||
|
});
|
||||||
|
$("#btn-submit").click(this.submit.bind(this));
|
||||||
|
}
|
||||||
|
submit(e) {
|
||||||
|
this.editor.save();
|
||||||
|
let results = Validator.bind("#div-form").validate();
|
||||||
|
if (results.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data = new FormData();
|
||||||
|
data.append('name', $("#name").val());
|
||||||
|
if ($("#type-input").prop("checked")) {
|
||||||
|
data.append('content', $('#txt-content').val());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let file = $('#file-content')[0];
|
||||||
|
data.append('content', file.files[0]);
|
||||||
|
}
|
||||||
|
let url = $(e.target).data("url") || "";
|
||||||
|
$ajax.post(url, data).encoder("none").trigger(e.target).json((r) => {
|
||||||
|
if (r.success) {
|
||||||
|
location.href = "/stack/archive/";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Notification.show("danger", `FAILED: ${r.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Archive.EditPage = EditPage;
|
||||||
|
})(Archive = Stack.Archive || (Stack.Archive = {}));
|
||||||
|
})(Stack = Swirl.Stack || (Swirl.Stack = {}));
|
||||||
|
})(Swirl || (Swirl = {}));
|
||||||
|
var Swirl;
|
||||||
(function (Swirl) {
|
(function (Swirl) {
|
||||||
var Stack;
|
var Stack;
|
||||||
(function (Stack) {
|
(function (Stack) {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -314,6 +314,7 @@ namespace Swirl.Core {
|
|||||||
switch (this.options.encoder) {
|
switch (this.options.encoder) {
|
||||||
case "none":
|
case "none":
|
||||||
settings.contentType = false;
|
settings.contentType = false;
|
||||||
|
settings.processData = false;
|
||||||
break;
|
break;
|
||||||
case "json":
|
case "json":
|
||||||
settings.contentType = "application/json; charset=UTF-8";
|
settings.contentType = "application/json; charset=UTF-8";
|
||||||
|
@ -8,7 +8,7 @@ namespace Swirl.Core {
|
|||||||
type InputElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLButtonElement;
|
type InputElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLButtonElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 输入控件验证结果
|
* The result of validation.
|
||||||
*
|
*
|
||||||
* @interface ValidationResult
|
* @interface ValidationResult
|
||||||
*/
|
*/
|
||||||
@ -32,7 +32,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTML5 表单元素原生验证器
|
* HTML5 form element native validator
|
||||||
*
|
*
|
||||||
* @class NativeRule
|
* @class NativeRule
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -45,7 +45,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 必填字段验证器
|
* Required validator
|
||||||
*
|
*
|
||||||
* @class RequiredRule
|
* @class RequiredRule
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -57,7 +57,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 必选字段验证器(用于 radio 和 checkbox), 示例: checked, checked(2), checked(1~2)
|
* Checked validator(for radio/checkbox), e.g. checked, checked(2), checked(1~2)
|
||||||
*
|
*
|
||||||
* @class CheckedRule
|
* @class CheckedRule
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -71,7 +71,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 电子邮箱验证器
|
* Email validator
|
||||||
*
|
*
|
||||||
* @class EmailValidator
|
* @class EmailValidator
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -85,7 +85,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP/FTP 地址验证器
|
* HTTP/FTP URL validator
|
||||||
*
|
*
|
||||||
* @class UrlValidator
|
* @class UrlValidator
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -99,7 +99,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IPV4 地址验证器
|
* IPV4 address validator
|
||||||
*
|
*
|
||||||
* @class IPValidator
|
* @class IPValidator
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -113,7 +113,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字段匹配验证器(如密码)
|
* Match validator(e.g. password confirmation)
|
||||||
*
|
*
|
||||||
* @class MatchValidator
|
* @class MatchValidator
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -125,7 +125,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串长度验证器
|
* String length validator.
|
||||||
*
|
*
|
||||||
* @class LengthValidator
|
* @class LengthValidator
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -155,14 +155,14 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串宽度验证器(中文字符宽度为2)
|
* String width validator, the width of CJK characters is considered as 2.
|
||||||
*
|
*
|
||||||
* @class WidthValidator
|
* @class WidthValidator
|
||||||
* @extends {LengthRule}
|
* @extends {LengthRule}
|
||||||
*/
|
*/
|
||||||
class WidthRule extends LengthRule {
|
class WidthRule extends LengthRule {
|
||||||
protected getLength(value: string): number {
|
protected getLength(value: string): number {
|
||||||
var doubleByteChars = value.match(/[^\x00-\xff]/ig);
|
let doubleByteChars = value.match(/[^\x00-\xff]/ig);
|
||||||
return value.length + (doubleByteChars == null ? 0 : doubleByteChars.length);
|
return value.length + (doubleByteChars == null ? 0 : doubleByteChars.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 正则表达式验证器
|
* Regex validator
|
||||||
*
|
*
|
||||||
* @class RegexValidator
|
* @class RegexValidator
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
@ -195,7 +195,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务器端验证器
|
* Remote validator
|
||||||
*
|
*
|
||||||
* @class RemoteRule
|
* @class RemoteRule
|
||||||
* @implements {ValidationRule}
|
* @implements {ValidationRule}
|
||||||
|
70
assets/swirl/ts/stack/archive/edit.ts
Normal file
70
assets/swirl/ts/stack/archive/edit.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
///<reference path="../../core/core.ts" />
|
||||||
|
namespace Swirl.Stack.Archive {
|
||||||
|
import Validator = Swirl.Core.Validator;
|
||||||
|
import AjaxResult = Swirl.Core.AjaxResult;
|
||||||
|
import Notification = Swirl.Core.Notification;
|
||||||
|
import ValidationRule = Swirl.Core.ValidationRule;
|
||||||
|
|
||||||
|
class ContentRequiredRule implements ValidationRule {
|
||||||
|
validate($form: JQuery, $input: JQuery, arg?: string): {ok: boolean, error?: string} {
|
||||||
|
let el = <HTMLInputElement>$input[0];
|
||||||
|
if ($("#type-" + arg).prop("checked")) {
|
||||||
|
console.log(el.value);
|
||||||
|
return {ok: el.checkValidity ? el.checkValidity() : true, error: el.validationMessage};
|
||||||
|
}
|
||||||
|
return {ok: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EditPage {
|
||||||
|
private editor: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
Validator.register("content", new ContentRequiredRule(), "");
|
||||||
|
|
||||||
|
this.editor = CodeMirror.fromTextArea($("#txt-content")[0], {lineNumbers: true});
|
||||||
|
|
||||||
|
$("#file-content").change(e => {
|
||||||
|
let file = <HTMLInputElement>e.target;
|
||||||
|
if (file.files.length > 0) {
|
||||||
|
$('#filename').text(file.files[0].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#type-input,#type-upload").click(e => {
|
||||||
|
let type = $(e.target).val();
|
||||||
|
$("#div-input").toggle(type == "input");
|
||||||
|
$("#div-upload").toggle(type == "upload");
|
||||||
|
});
|
||||||
|
$("#btn-submit").click(this.submit.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private submit(e: JQueryEventObject) {
|
||||||
|
this.editor.save();
|
||||||
|
|
||||||
|
let results = Validator.bind("#div-form").validate();
|
||||||
|
if (results.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = new FormData();
|
||||||
|
data.append('name', $("#name").val());
|
||||||
|
if ($("#type-input").prop("checked")) {
|
||||||
|
data.append('content', $('#txt-content').val());
|
||||||
|
} else {
|
||||||
|
let file = <HTMLInputElement>$('#file-content')[0];
|
||||||
|
data.append('content', file.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = $(e.target).data("url") || "";
|
||||||
|
$ajax.post(url, data).encoder("none").trigger(e.target).json((r: AjaxResult) => {
|
||||||
|
if (r.success) {
|
||||||
|
location.href = "/stack/archive/"
|
||||||
|
} else {
|
||||||
|
Notification.show("danger", `FAILED: ${r.message}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var CodeMirror: any;
|
@ -173,16 +173,18 @@ func stackArchiveNew(ctx web.Context) error {
|
|||||||
func stackArchiveCreate(ctx web.Context) error {
|
func stackArchiveCreate(ctx web.Context) error {
|
||||||
archive := &model.Archive{}
|
archive := &model.Archive{}
|
||||||
err := ctx.Bind(archive)
|
err := ctx.Bind(archive)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
// Validate format
|
return err
|
||||||
_, err = compose.Parse(archive.Name, archive.Content)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
archive.CreatedBy = ctx.User().ID()
|
|
||||||
archive.UpdatedBy = archive.CreatedBy
|
|
||||||
err = biz.Archive.Create(archive)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate format
|
||||||
|
_, err = compose.Parse(archive.Name, archive.Content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.CreatedBy = ctx.User().ID()
|
||||||
|
archive.UpdatedBy = archive.CreatedBy
|
||||||
|
err = biz.Archive.Create(archive)
|
||||||
return ajaxResult(ctx, err)
|
return ajaxResult(ctx, err)
|
||||||
}
|
}
|
||||||
|
2
main.go
2
main.go
@ -23,7 +23,7 @@ func main() {
|
|||||||
misc.BindOptions()
|
misc.BindOptions()
|
||||||
|
|
||||||
app.Name = "Swirl"
|
app.Name = "Swirl"
|
||||||
app.Version = "0.6.1"
|
app.Version = "0.6.2"
|
||||||
app.Desc = "A web management UI for Docker, focused on swarm cluster"
|
app.Desc = "A web management UI for Docker, focused on swarm cluster"
|
||||||
app.Action = func(ctx *app.Context) {
|
app.Action = func(ctx *app.Context) {
|
||||||
misc.LoadOptions()
|
misc.LoadOptions()
|
||||||
|
@ -3,9 +3,9 @@ package model
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Archive struct {
|
type Archive struct {
|
||||||
ID string `bson:"_id" json:"id,omitempty"`
|
ID string `bson:"_id" json:"id,omitempty" bind:"id=path"`
|
||||||
Name string `bson:"name" json:"name,omitempty"`
|
Name string `bson:"name" json:"name,omitempty"`
|
||||||
Content string `bson:"content" json:"content,omitempty"`
|
Content string `bson:"content" json:"content,omitempty" bind:"content=form,content=file"`
|
||||||
CreatedBy string `bson:"created_by" json:"created_by,omitempty"`
|
CreatedBy string `bson:"created_by" json:"created_by,omitempty"`
|
||||||
CreatedAt time.Time `bson:"created_at" json:"created_at,omitempty"`
|
CreatedAt time.Time `bson:"created_at" json:"created_at,omitempty"`
|
||||||
UpdatedBy string `bson:"updated_by" json:"updated_by,omitempty"`
|
UpdatedBy string `bson:"updated_by" json:"updated_by,omitempty"`
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
<nav class="tabs is-boxed">
|
<nav class="tabs is-boxed">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/stack/task/">Tasks</a>
|
<a href="/stack/task/">{{ i18n("menu.stack.task") }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="is-active">
|
<li class="is-active">
|
||||||
<a href="/stack/archive/">Archives</a>
|
<a href="/stack/archive/">{{ i18n("menu.stack.archive") }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@ -36,9 +36,9 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">Dashboard</a></li>
|
<li><a href="/">{{ i18n("menu.dashboard") }}</a></li>
|
||||||
<li><a href="/stack/archive/">Archives</a></li>
|
<li><a href="/stack/archive/">{{ i18n("menu.stack.archive") }}</a></li>
|
||||||
<li class="is-active"><a>Detail</a></li>
|
<li class="is-active"><a>{{ i18n("menu.detail") }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@ -55,8 +55,8 @@
|
|||||||
<nav class="navbar has-shadow">
|
<nav class="navbar has-shadow">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item is-tab is-active" href="/stack/archive/{{.Archive.ID}}/detail">Detail</a>
|
<a class="navbar-item is-tab is-active" href="/stack/archive/{{.Archive.ID}}/detail">{{ i18n("menu.detail") }}</a>
|
||||||
<a class="navbar-item is-tab" href="/stack/archive/{{.Archive.ID}}/edit">Edit</a>
|
<a class="navbar-item is-tab" href="/stack/archive/{{.Archive.ID}}/edit">{{ i18n("menu.edit") }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
@ -64,16 +64,16 @@
|
|||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Created at</dt>
|
<dt>{{ i18n("field.created-at") }}</dt>
|
||||||
<dd>{{ time(.Archive.CreatedAt) }}</dd>
|
<dd>{{ time(.Archive.CreatedAt) }}</dd>
|
||||||
<dt>Updated at</dt>
|
<dt>{{ i18n("field.updated-at") }}</dt>
|
||||||
<dd>{{ time(.Archive.UpdatedAt) }}</dd>
|
<dd>{{ time(.Archive.UpdatedAt) }}</dd>
|
||||||
<dt>Content</dt>
|
<dt>Content</dt>
|
||||||
<dd class="content"><pre class="is-paddingless"><code class="yaml">{{ .Archive.Content }}</code></pre></dd>
|
<dd class="content"><pre class="is-paddingless"><code class="yaml">{{ .Archive.Content }}</code></pre></dd>
|
||||||
</dl>
|
</dl>
|
||||||
<a href="/stack/archive/" class="button is-primary">
|
<a href="/stack/archive/" class="button is-primary">
|
||||||
<span class="icon"><i class="fa fa-reply"></i></span>
|
<span class="icon"><i class="fa fa-reply"></i></span>
|
||||||
<span>Return</span>
|
<span>{{ i18n("button.return") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{{ extends "../../_layouts/default" }}
|
{{ extends "../../_layouts/default" }}
|
||||||
|
{{ import "../../_modules/form" }}
|
||||||
|
|
||||||
{{ block style() }}
|
{{ block style() }}
|
||||||
<link rel="stylesheet" href="/assets/codemirror/codemirror.css?v=5.30">
|
<link rel="stylesheet" href="/assets/codemirror/codemirror.css?v=5.30">
|
||||||
@ -7,7 +8,7 @@
|
|||||||
{{ block script() }}
|
{{ block script() }}
|
||||||
<script src="/assets/codemirror/codemirror.js?v=5.30"></script>
|
<script src="/assets/codemirror/codemirror.js?v=5.30"></script>
|
||||||
<script src="/assets/codemirror/mode/yaml.js?v=5.30"></script>
|
<script src="/assets/codemirror/mode/yaml.js?v=5.30"></script>
|
||||||
<script>var editor = CodeMirror.fromTextArea(document.getElementById("txt-content"), {lineNumbers: true});</script>
|
<script>$(() => new Swirl.Stack.Archive.EditPage())</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ block body() }}
|
{{ block body() }}
|
||||||
@ -64,30 +65,48 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div id="div-form" class="container">
|
||||||
<form method="post" action="update" data-form="ajax-json" data-url="/stack/archive/">
|
<div class="field">
|
||||||
<input name="id" value="{{ .Archive.ID }}" type="hidden">
|
<label class="label">{{ i18n("field.name") }}</label>
|
||||||
<div class="field">
|
<div class="control">
|
||||||
<label class="label">{{ i18n("field.name") }}</label>
|
<input id="name" name="name" class="input" value="{{ .Archive.Name }}" type="text" placeholder="" data-v-rule="native;regex" data-v-arg-regex="^[a-z0-9_-]+$" data-v-msg-regex="Name can contain only letters, digits, '_' and '-'." required>
|
||||||
<div class="control">
|
|
||||||
<input name="name" class="input" value="{{ .Archive.Name }}" type="text" placeholder="" data-v-rule="native;regex" data-v-arg-regex="^[a-z0-9_-]+$" data-v-msg-regex="Name can contain only letters, digits, '_' and '-'." required>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label">Content</label>
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<textarea id="txt-content" name="content" class="textarea code" rows="20" placeholder="Compose file content" data-v-rule="native" required>{{ .Archive.Content }}</textarea>
|
{{ yield radio(name="type", value="input", label="Input", checked="input") }}
|
||||||
</div>
|
{{ yield radio(name="type", value="upload", label="Upload") }}
|
||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped">
|
</div>
|
||||||
<div class="control">
|
<div id="div-input" class="field">
|
||||||
<button type="submit" class="button is-primary">{{ i18n("button.submit") }}</button>
|
<label class="label">Content</label>
|
||||||
</div>
|
<div class="control">
|
||||||
<div class="control">
|
<textarea id="txt-content" name="content" class="textarea" rows="20" placeholder="Compose file content" data-v-rule="content" data-v-arg-content="input" required>{{ .Archive.Content }}</textarea>
|
||||||
<a href="/stack/archive/" class="button is-link">{{ i18n("button.cancel") }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
<div id="div-upload" class="field" style="display: none">
|
||||||
|
<label class="label">Content</label>
|
||||||
|
<div class="file has-name is-fullwidth">
|
||||||
|
<label class="file-label">
|
||||||
|
<input id="file-content" name="content" class="file-input" type="file" data-v-rule="content" data-v-arg-content="upload" required>
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<i class="fa fa-upload"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">Choose a file…</span>
|
||||||
|
</span>
|
||||||
|
<span id="filename" class="file-name"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button id="btn-submit" type="submit" class="button is-primary" data-url="update">{{ i18n("button.submit") }}</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a href="/stack/archive/" class="button is-link">{{ i18n("button.cancel") }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{{ end }}
|
{{ end }}
|
@ -1,4 +1,5 @@
|
|||||||
{{ extends "../../_layouts/default" }}
|
{{ extends "../../_layouts/default" }}
|
||||||
|
{{ import "../../_modules/form" }}
|
||||||
|
|
||||||
{{ block style() }}
|
{{ block style() }}
|
||||||
<link rel="stylesheet" href="/assets/codemirror/codemirror.css?v=5.30">
|
<link rel="stylesheet" href="/assets/codemirror/codemirror.css?v=5.30">
|
||||||
@ -7,7 +8,7 @@
|
|||||||
{{ block script() }}
|
{{ block script() }}
|
||||||
<script src="/assets/codemirror/codemirror.js?v=5.30"></script>
|
<script src="/assets/codemirror/codemirror.js?v=5.30"></script>
|
||||||
<script src="/assets/codemirror/mode/yaml.js?v=5.30"></script>
|
<script src="/assets/codemirror/mode/yaml.js?v=5.30"></script>
|
||||||
<script>var editor = CodeMirror.fromTextArea(document.getElementById("txt-content"), {lineNumbers: true});</script>
|
<script>$(() => new Swirl.Stack.Archive.EditPage())</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ block body() }}
|
{{ block body() }}
|
||||||
@ -34,31 +35,50 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div id="div-form" class="container">
|
||||||
<h2 class="title">Create stack archive</h2>
|
<h2 class="title">Create stack archive</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<form method="post" data-form="ajax-json" data-url="/stack/archive/">
|
<div class="field">
|
||||||
<div class="field">
|
<label class="label">{{ i18n("field.name") }}</label>
|
||||||
<label class="label">{{ i18n("field.name") }}</label>
|
<div class="control">
|
||||||
<div class="control">
|
<input id="name" name="name" class="input" type="text" placeholder="" data-v-rule="native;regex" data-v-arg-regex="^[a-z0-9_-]+$" data-v-msg-regex="Name can contain only letters, digits, '_' and '-'." required>
|
||||||
<input name="name" class="input" type="text" placeholder="" data-v-rule="native;regex" data-v-arg-regex="^[a-z0-9_-]+$" data-v-msg-regex="Name can contain only letters, digits, '_' and '-'." required>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label">Content</label>
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<textarea id="txt-content" name="content" class="textarea" rows="20" placeholder="Compose file content" data-v-rule="native" required></textarea>
|
{{ yield radio(name="type", value="input", label="Input", checked="input") }}
|
||||||
</div>
|
{{ yield radio(name="type", value="upload", label="Upload") }}
|
||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped">
|
</div>
|
||||||
<div class="control">
|
<div id="div-input" class="field">
|
||||||
<button type="submit" class="button is-primary">{{ i18n("button.submit") }}</button>
|
<label class="label">Content</label>
|
||||||
</div>
|
<div class="control">
|
||||||
<div class="control">
|
<textarea id="txt-content" name="content" class="textarea" rows="20" placeholder="Compose file content" data-v-rule="content" data-v-arg-content="input" required></textarea>
|
||||||
<a href="/stack/archive/" class="button is-link">{{ i18n("button.cancel") }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
<div id="div-upload" class="field" style="display: none">
|
||||||
|
<label class="label">Content</label>
|
||||||
|
<div class="file has-name is-fullwidth">
|
||||||
|
<label class="file-label">
|
||||||
|
<input id="file-content" name="content" class="file-input" type="file" data-v-rule="content" data-v-arg-content="upload" required>
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<i class="fa fa-upload"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">Choose a file…</span>
|
||||||
|
</span>
|
||||||
|
<span id="filename" class="file-name"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button id="btn-submit" type="submit" class="button is-primary">{{ i18n("button.submit") }}</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a href="/stack/archive/" class="button is-link">{{ i18n("button.cancel") }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{{ end }}
|
{{ end }}
|
Loading…
Reference in New Issue
Block a user