init
This commit is contained in:
165
user/plugins/form/.eslintrc
Normal file
165
user/plugins/form/.eslintrc
Normal file
@@ -0,0 +1,165 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"accessor-pairs": 2,
|
||||
"array-bracket-spacing": 0,
|
||||
"block-scoped-var": 0,
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"camelcase": 0,
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2, { "before": false, "after": true }],
|
||||
"comma-style": [2, "last"],
|
||||
"complexity": 0,
|
||||
"computed-property-spacing": 0,
|
||||
"consistent-return": 0,
|
||||
"consistent-this": 0,
|
||||
"constructor-super": 2,
|
||||
"curly": [2, "multi-line"],
|
||||
"default-case": 0,
|
||||
"dot-location": [2, "property"],
|
||||
"dot-notation": 0,
|
||||
"eol-last": 2,
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"func-names": 0,
|
||||
"func-style": 0,
|
||||
"generator-star-spacing": [2, { "before": true, "after": true }],
|
||||
"guard-for-in": 0,
|
||||
"handle-callback-err": [2, "^(err|error)$" ],
|
||||
"indent": [2, 4, { "SwitchCase": 1 }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"linebreak-style": 0,
|
||||
"lines-around-comment": 0,
|
||||
"max-nested-callbacks": 0,
|
||||
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
|
||||
"new-parens": 2,
|
||||
"newline-after-var": 0,
|
||||
"no-alert": 0,
|
||||
"no-array-constructor": 2,
|
||||
"no-caller": 2,
|
||||
"no-catch-shadow": 0,
|
||||
"no-cond-assign": 2,
|
||||
"no-console": 0,
|
||||
"no-constant-condition": 0,
|
||||
"no-continue": 0,
|
||||
"no-control-regex": 2,
|
||||
"no-debugger": 2,
|
||||
"no-delete-var": 2,
|
||||
"no-div-regex": 0,
|
||||
"no-dupe-args": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-else-return": 0,
|
||||
"no-empty": 0,
|
||||
"no-empty-character-class": 2,
|
||||
"no-eq-null": 0,
|
||||
"no-eval": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-extra-boolean-cast": 2,
|
||||
"no-extra-parens": 0,
|
||||
"no-extra-semi": 0,
|
||||
"no-fallthrough": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-func-assign": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-inline-comments": 0,
|
||||
"no-inner-declarations": [2, "functions"],
|
||||
"no-invalid-regexp": 2,
|
||||
"no-irregular-whitespace": 2,
|
||||
"no-iterator": 2,
|
||||
"no-label-var": 2,
|
||||
"no-labels": 2,
|
||||
"no-lone-blocks": 2,
|
||||
"no-lonely-if": 0,
|
||||
"no-loop-func": 0,
|
||||
"no-mixed-requires": 0,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multi-spaces": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-multiple-empty-lines": [2, { "max": 1 }],
|
||||
"no-native-reassign": 2,
|
||||
"no-negated-in-lhs": 2,
|
||||
"no-nested-ternary": 0,
|
||||
"no-new": 2,
|
||||
"no-new-func": 0,
|
||||
"no-new-object": 2,
|
||||
"no-new-require": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-obj-calls": 2,
|
||||
"no-octal": 2,
|
||||
"no-octal-escape": 2,
|
||||
"no-param-reassign": 0,
|
||||
"no-path-concat": 0,
|
||||
"no-process-env": 0,
|
||||
"no-process-exit": 0,
|
||||
"no-proto": 0,
|
||||
"no-redeclare": 2,
|
||||
"no-regex-spaces": 2,
|
||||
"no-restricted-modules": 0,
|
||||
"no-return-assign": 2,
|
||||
"no-script-url": 0,
|
||||
"no-self-compare": 2,
|
||||
"no-sequences": 2,
|
||||
"no-shadow": 0,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-spaced-func": 2,
|
||||
"no-sparse-arrays": 2,
|
||||
"no-sync": 0,
|
||||
"no-ternary": 0,
|
||||
"no-this-before-super": 2,
|
||||
"no-throw-literal": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-undef": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-undefined": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-unexpected-multiline": 2,
|
||||
"no-unneeded-ternary": 2,
|
||||
"no-unreachable": 2,
|
||||
"no-unused-expressions": 0,
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
|
||||
"no-use-before-define": 0,
|
||||
"no-var": 0,
|
||||
"no-void": 0,
|
||||
"no-warning-comments": 0,
|
||||
"no-with": 2,
|
||||
"object-curly-spacing": 0,
|
||||
"object-shorthand": 0,
|
||||
"one-var": [2, { "initialized": "never" }],
|
||||
"operator-assignment": 0,
|
||||
"operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }],
|
||||
"padded-blocks": 0,
|
||||
"prefer-const": 0,
|
||||
"quote-props": 0,
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"radix": 2,
|
||||
"semi": [2, "always"],
|
||||
"semi-spacing": 0,
|
||||
"sort-vars": 0,
|
||||
"keyword-spacing": [2, {"after": true, "overrides": {"throw": { "after": true}, "return": { "before": true }}}],
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }],
|
||||
"strict": 0,
|
||||
"use-isnan": 2,
|
||||
"valid-jsdoc": 0,
|
||||
"valid-typeof": 2,
|
||||
"vars-on-top": 0,
|
||||
"wrap-iife": [2, "any"],
|
||||
"wrap-regex": 0,
|
||||
"yoda": [2, "never"]
|
||||
}
|
||||
}
|
||||
5
user/plugins/form/.gitignore
vendored
Normal file
5
user/plugins/form/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# OS Generated
|
||||
.DS_Store*
|
||||
/.idea
|
||||
node_modules
|
||||
*.js.map
|
||||
1233
user/plugins/form/CHANGELOG.md
Normal file
1233
user/plugins/form/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
21
user/plugins/form/LICENSE
Normal file
21
user/plugins/form/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Grav
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
41
user/plugins/form/README.md
Normal file
41
user/plugins/form/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Grav Form Plugin
|
||||
|
||||
The **form plugin** for [Grav](https://github.com/getgrav/grav) adds the ability to create and use forms. This is currently used extensively by the **admin** and **login** plugins.
|
||||
|
||||
# Installation
|
||||
|
||||
The form plugin is easy to install with GPM.
|
||||
|
||||
```
|
||||
$ bin/gpm install form
|
||||
```
|
||||
|
||||
# Configuration
|
||||
|
||||
Simply copy the `user/plugins/form/form.yaml` into `user/config/plugins/form.yaml` and make your modifications.
|
||||
|
||||
```
|
||||
enabled: true
|
||||
```
|
||||
|
||||
# How to use the Form Plugin
|
||||
|
||||
The Learn site has two pages describing how to use the Form Plugin:
|
||||
- [Forms](https://learn.getgrav.org/forms)
|
||||
- [Add a contact form](https://learn.getgrav.org/forms/forms/example-form)
|
||||
|
||||
# Using email
|
||||
|
||||
Note: when using email functionality in your forms, make sure you have configured the Email plugin correctly. In particular, make sure you configured the "Email from" and "Email to" email addresses in the Email plugin with your email address.
|
||||
|
||||
# NOTES:
|
||||
|
||||
As of version **Form 6.0.0** forms are no longer initialized before caching, but when the form is requested. This has been done to make dynamic forms to work better with caching. There may be some backward compatibility issues for logic that modifies pages with forms as the modification doesn't happen without accessing the form first.
|
||||
|
||||
As of version **Form 5.0.0** Grav 1.7+ is required.
|
||||
|
||||
As of version **Form 4.0.6**, form labels are now being output with the `|raw` filter. If you wish to show HTML in your form label, ie `Root Folder <root>`, then you need to escape that in your form definition:
|
||||
|
||||
```yaml
|
||||
label: Root Folder <root>
|
||||
```
|
||||
200
user/plugins/form/app/fields/array.js
Normal file
200
user/plugins/form/app/fields/array.js
Normal file
@@ -0,0 +1,200 @@
|
||||
import $ from 'jquery';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
let body = $('body');
|
||||
|
||||
class Template {
|
||||
constructor(container) {
|
||||
this.container = $(container);
|
||||
|
||||
if (this.getName() === undefined) {
|
||||
this.container = this.container.closest('[data-grav-array-name]');
|
||||
}
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.container.data('grav-array-name') || '';
|
||||
}
|
||||
|
||||
getKeyPlaceholder() {
|
||||
return this.container.data('grav-array-keyname') || 'Key';
|
||||
}
|
||||
|
||||
getValuePlaceholder() {
|
||||
return this.container.data('grav-array-valuename') || 'Value';
|
||||
}
|
||||
|
||||
isValueOnly() {
|
||||
return this.container.find('[data-grav-array-mode="value_only"]:first').length || false;
|
||||
}
|
||||
|
||||
isTextArea() {
|
||||
return this.container.data('grav-array-textarea') || false;
|
||||
}
|
||||
|
||||
shouldBeDisabled() {
|
||||
// check for toggleables, if field is toggleable and it's not enabled, render disabled
|
||||
let toggle = this.container.closest('.form-field').find('[data-grav-field="toggleable"] input[type="checkbox"]');
|
||||
return toggle.length && toggle.is(':not(:checked)');
|
||||
}
|
||||
|
||||
getNewRow() {
|
||||
let tpl = '';
|
||||
const value = this.isTextArea()
|
||||
? `<textarea ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" name="" placeholder="${this.getValuePlaceholder()}"></textarea>`
|
||||
: `<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" type="text" name="" value="" placeholder="${this.getValuePlaceholder()}" />`;
|
||||
|
||||
if (this.isValueOnly()) {
|
||||
tpl += `
|
||||
<div class="form-row array-field-value_only" data-grav-array-type="row">
|
||||
<span data-grav-array-action="sort" class="fa fa-bars"></span>
|
||||
${value}
|
||||
`;
|
||||
} else {
|
||||
tpl += `
|
||||
<div class="form-row" data-grav-array-type="row">
|
||||
<span data-grav-array-action="sort" class="fa fa-bars"></span>
|
||||
<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="key" type="text" value="" placeholder="${this.getKeyPlaceholder()}" />
|
||||
${value}
|
||||
`;
|
||||
}
|
||||
|
||||
tpl += `
|
||||
<span data-grav-array-action="rem" class="fa fa-minus"></span>
|
||||
<span data-grav-array-action="add" class="fa fa-plus"></span>
|
||||
</div>`;
|
||||
|
||||
return tpl;
|
||||
}
|
||||
}
|
||||
|
||||
export default class ArrayField {
|
||||
constructor() {
|
||||
body.on('input', '[data-grav-array-type="key"], [data-grav-array-type="value"]', (event) => this.actionInput(event));
|
||||
body.on('click touch', '[data-grav-array-action]:not([data-grav-array-action="sort"])', (event) => this.actionEvent(event));
|
||||
|
||||
this.arrays = $();
|
||||
|
||||
$('[data-grav-field="array"]').each((index, list) => this.addArray(list));
|
||||
$('body').on('mutation._grav', this._onAddedNodes.bind(this));
|
||||
}
|
||||
|
||||
addArray(list) {
|
||||
list = $(list);
|
||||
|
||||
list.find('[data-grav-array-type="container"]').each((index, container) => {
|
||||
container = $(container);
|
||||
if (container.data('array-sort') || container[0].hasAttribute('data-array-nosort')) { return; }
|
||||
|
||||
container.data('array-sort', new Sortable(container.get(0), {
|
||||
handle: '.fa-bars',
|
||||
animation: 150
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
actionInput(event) {
|
||||
let element = $(event.target);
|
||||
let type = element.data('grav-array-type');
|
||||
|
||||
this._setTemplate(element);
|
||||
|
||||
let template = element.data('array-template');
|
||||
let keyElement = type === 'key' ? element : element.siblings('[data-grav-array-type="key"]:first');
|
||||
let valueElement = type === 'value' ? element : element.siblings('[data-grav-array-type="value"]:first');
|
||||
|
||||
let escaped_name = !template.isValueOnly() ? keyElement.val() : this.getIndexFor(element);
|
||||
escaped_name = escaped_name.toString().replace(/\[/g, '%5B').replace(/]/g, '%5D');
|
||||
let name = `${template.getName()}[${escaped_name}]`;
|
||||
|
||||
if (!template.isValueOnly() && (!keyElement.val() && !valueElement.val())) {
|
||||
valueElement.attr('name', '');
|
||||
} else {
|
||||
// valueElement.attr('name', !valueElement.val() ? template.getName() : name);
|
||||
valueElement.attr('name', name);
|
||||
}
|
||||
|
||||
this.refreshNames(template);
|
||||
}
|
||||
|
||||
actionEvent(event) {
|
||||
event && event.preventDefault();
|
||||
let element = $(event.target);
|
||||
let action = element.data('grav-array-action');
|
||||
let container = element.parents('[data-grav-array-type="container"]');
|
||||
|
||||
this._setTemplate(element);
|
||||
|
||||
this[`${action}Action`](element);
|
||||
|
||||
let siblings = container.find('> div');
|
||||
container[siblings.length > 1 ? 'removeClass' : 'addClass']('one-child');
|
||||
}
|
||||
|
||||
addAction(element) {
|
||||
let template = element.data('array-template');
|
||||
let row = element.closest('[data-grav-array-type="row"]');
|
||||
|
||||
row.after(template.getNewRow());
|
||||
}
|
||||
|
||||
remAction(element) {
|
||||
let template = element.data('array-template');
|
||||
let row = element.closest('[data-grav-array-type="row"]');
|
||||
let isLast = !row.siblings().length;
|
||||
|
||||
if (isLast) {
|
||||
let newRow = $(template.getNewRow());
|
||||
row.after(newRow);
|
||||
newRow.find('[data-grav-array-type="value"]:last').attr('name', template.getName());
|
||||
}
|
||||
|
||||
row.remove();
|
||||
this.refreshNames(template);
|
||||
}
|
||||
|
||||
refreshNames(template) {
|
||||
if (!template.isValueOnly()) { return; }
|
||||
|
||||
let row = template.container.find('> div > [data-grav-array-type="row"]');
|
||||
let inputs = row.find('[name]:not([name=""])');
|
||||
|
||||
inputs.each((index, input) => {
|
||||
input = $(input);
|
||||
let name = input.attr('name');
|
||||
name = name.replace(/\[\d+\]$/, `[${index}]`);
|
||||
input.attr('name', name);
|
||||
});
|
||||
|
||||
if (!inputs.length) {
|
||||
row.find('[data-grav-array-type="value"]').attr('name', template.getName());
|
||||
}
|
||||
}
|
||||
|
||||
getIndexFor(element) {
|
||||
let template = element.data('array-template');
|
||||
let row = element.closest('[data-grav-array-type="row"]');
|
||||
|
||||
return template.container.find(`${template.isValueOnly() ? '> div ' : ''} > [data-grav-array-type="row"]`).index(row);
|
||||
}
|
||||
|
||||
_setTemplate(element) {
|
||||
if (!element.data('array-template')) {
|
||||
element.data('array-template', new Template(element.closest('[data-grav-array-name]')));
|
||||
}
|
||||
}
|
||||
|
||||
_onAddedNodes(event, target/* , record, instance */) {
|
||||
let arrays = $(target).find('[data-grav-field="array"]');
|
||||
if (!arrays.length) { return; }
|
||||
|
||||
arrays.each((index, list) => {
|
||||
list = $(list);
|
||||
if (!~this.arrays.index(list)) {
|
||||
this.addArray(list);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export let Instance = new ArrayField();
|
||||
340
user/plugins/form/app/fields/file.js
Normal file
340
user/plugins/form/app/fields/file.js
Normal file
@@ -0,0 +1,340 @@
|
||||
import $ from 'jquery';
|
||||
import Dropzone from 'dropzone';
|
||||
// import EXIF from 'exif-js';
|
||||
import {config, translations} from 'grav-form';
|
||||
|
||||
// translations
|
||||
const Dictionary = {
|
||||
dictCancelUpload: translations.PLUGIN_FORM.DROPZONE_CANCEL_UPLOAD,
|
||||
dictCancelUploadConfirmation: translations.PLUGIN_FORM.DROPZONE_CANCEL_UPLOAD_CONFIRMATION,
|
||||
dictDefaultMessage: translations.PLUGIN_FORM.DROPZONE_DEFAULT_MESSAGE,
|
||||
dictFallbackMessage: translations.PLUGIN_FORM.DROPZONE_FALLBACK_MESSAGE,
|
||||
dictFallbackText: translations.PLUGIN_FORM.DROPZONE_FALLBACK_TEXT,
|
||||
dictFileTooBig: translations.PLUGIN_FORM.DROPZONE_FILE_TOO_BIG,
|
||||
dictInvalidFileType: translations.PLUGIN_FORM.DROPZONE_INVALID_FILE_TYPE,
|
||||
dictMaxFilesExceeded: translations.PLUGIN_FORM.DROPZONE_MAX_FILES_EXCEEDED,
|
||||
dictRemoveFile: translations.PLUGIN_FORM.DROPZONE_REMOVE_FILE,
|
||||
dictRemoveFileConfirmation: translations.PLUGIN_FORM.DROPZONE_REMOVE_FILE_CONFIRMATION,
|
||||
dictResponseError: translations.PLUGIN_FORM.DROPZONE_RESPONSE_ERROR
|
||||
};
|
||||
|
||||
Dropzone.autoDiscover = false;
|
||||
|
||||
const DropzoneMediaConfig = {
|
||||
createImageThumbnails: {thumbnailWidth: 150},
|
||||
addRemoveLinks: false,
|
||||
dictDefaultMessage: Dictionary.dictDefaultMessage,
|
||||
dictRemoveFileConfirmation: Dictionary.dictRemoveFileConfirmation,
|
||||
previewTemplate: ''
|
||||
};
|
||||
|
||||
// window.EXIF = EXIF;
|
||||
|
||||
export default class FilesField {
|
||||
constructor({container = '.dropzone.files-upload', options = {}} = {}) {
|
||||
this.container = $(container);
|
||||
if (!this.container.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.urls = {};
|
||||
DropzoneMediaConfig.previewTemplate = $('#dropzone-template').html();
|
||||
this.options = Object.assign({}, Dictionary, DropzoneMediaConfig, {
|
||||
klass: this,
|
||||
url: this.container.data('file-url-add') || config.current_url,
|
||||
acceptedFiles: this.container.data('media-types'),
|
||||
init: this.initDropzone
|
||||
}, this.container.data('dropzone-options'), options);
|
||||
|
||||
this.dropzone = new Dropzone(container, this.options);
|
||||
this.dropzone.on('complete', this.onDropzoneComplete.bind(this));
|
||||
this.dropzone.on('success', this.onDropzoneSuccess.bind(this));
|
||||
this.dropzone.on('removedfile', this.onDropzoneRemovedFile.bind(this));
|
||||
this.dropzone.on('sending', this.onDropzoneSending.bind(this));
|
||||
this.dropzone.on('error', this.onDropzoneError.bind(this));
|
||||
}
|
||||
|
||||
initDropzone() {
|
||||
let files = this.options.klass.container.find('[data-file]');
|
||||
let dropzone = this;
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
files.each((index, file) => {
|
||||
file = $(file);
|
||||
let data = file.data('file');
|
||||
let mock = {
|
||||
name: data.name,
|
||||
size: data.size,
|
||||
type: data.type,
|
||||
status: Dropzone.ADDED,
|
||||
accepted: true,
|
||||
url: this.options.url,
|
||||
removeUrl: data.remove,
|
||||
data
|
||||
};
|
||||
|
||||
dropzone.files.push(mock);
|
||||
dropzone.options.addedfile.call(dropzone, mock);
|
||||
if (mock.type.match(/^image\//)) dropzone.options.thumbnail.call(dropzone, mock, data.path);
|
||||
|
||||
file.remove();
|
||||
});
|
||||
}
|
||||
|
||||
getURI() {
|
||||
return this.container.data('mediaUri') || '';
|
||||
}
|
||||
|
||||
onDropzoneSending(file, xhr, formData) {
|
||||
const form = this.container.closest('form');
|
||||
const unique_id = form.find('[name="__unique_form_id__"]');
|
||||
formData.append('__form-name__', form.find('[name="__form-name__"]').val());
|
||||
if (unique_id.length) {
|
||||
formData.append('__unique_form_id__', unique_id.val());
|
||||
}
|
||||
formData.append('__form-file-uploader__', 1);
|
||||
formData.append('name', this.options.dotNotation);
|
||||
formData.append('form-nonce', config.form_nonce);
|
||||
formData.append('task', 'filesupload');
|
||||
formData.append('uri', this.getURI());
|
||||
}
|
||||
|
||||
onDropzoneSuccess(file, response, xhr) {
|
||||
if (this.options.reloadPage) {
|
||||
global.location.reload();
|
||||
}
|
||||
|
||||
// store params for removing file from session before it gets saved
|
||||
if (response.session) {
|
||||
file.sessionParams = response.session;
|
||||
file.removeUrl = this.options.url;
|
||||
|
||||
// Touch field value to force a mutation detection
|
||||
const input = this.container.find('[name][type="hidden"]');
|
||||
const value = input.val();
|
||||
input.val(value + ' ');
|
||||
}
|
||||
|
||||
return this.handleError({
|
||||
file,
|
||||
data: response,
|
||||
mode: 'removeFile',
|
||||
msg: `<p>${translations.PLUGIN_FORM.FILE_ERROR_UPLOAD} <strong>${file.name}</strong></p>
|
||||
<pre>${response.message}</pre>`
|
||||
});
|
||||
}
|
||||
|
||||
onDropzoneComplete(file) {
|
||||
if (!file.accepted && !file.rejected) {
|
||||
let data = {
|
||||
status: 'error',
|
||||
message: `${translations.PLUGIN_FORM.FILE_UNSUPPORTED}: ${file.name.match(/\..+/).join('')}`
|
||||
};
|
||||
|
||||
return this.handleError({
|
||||
file,
|
||||
data,
|
||||
mode: 'removeFile',
|
||||
msg: `<p>${translations.PLUGIN_FORM.FILE_ERROR_ADD} <strong>${file.name}</strong></p>
|
||||
<pre>${data.message}</pre>`
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.reloadPage) {
|
||||
global.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
onDropzoneRemovedFile(file, ...extra) {
|
||||
if (!file.accepted || file.rejected) {
|
||||
return;
|
||||
}
|
||||
const form = this.container.closest('form');
|
||||
const unique_id = form.find('[name="__unique_form_id__"]');
|
||||
let url = file.removeUrl || this.urls.delete || `${location.href}.json`;
|
||||
let path = (url || '').match(/path:(.*)\//);
|
||||
let data = new FormData();
|
||||
|
||||
data.append('filename', file.name);
|
||||
data.append('__form-name__', form.find('[name="__form-name__"]').val());
|
||||
data.append('name', this.options.dotNotation);
|
||||
data.append('form-nonce', config.form_nonce);
|
||||
data.append('uri', this.getURI());
|
||||
|
||||
if (file.sessionParams) {
|
||||
data.append('__form-file-remover__', '1');
|
||||
data.append('session', file.sessionParams);
|
||||
}
|
||||
|
||||
if (unique_id.length) {
|
||||
data.append('__unique_form_id__', unique_id.val());
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url,
|
||||
data,
|
||||
method: 'POST',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: () => {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
path = global.atob(path[1]);
|
||||
let input = this.container.find('[name][type="hidden"]');
|
||||
let data = JSON.parse(input.val() || '{}');
|
||||
delete data[path];
|
||||
input.val(JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onDropzoneError(file, response, xhr) {
|
||||
let message = xhr && response.error ? response.error.message : response;
|
||||
$(file.previewElement).find('[data-dz-errormessage]').html(message);
|
||||
|
||||
return this.handleError({
|
||||
file,
|
||||
data: {status: 'error'},
|
||||
msg: `<pre>${message}</pre>`
|
||||
});
|
||||
}
|
||||
|
||||
handleError(options) {
|
||||
return true;
|
||||
/* let { file, data, mode, msg } = options;
|
||||
if (data.status !== 'error' && data.status !== 'unauthorized') { return; }
|
||||
|
||||
switch (mode) {
|
||||
case 'addBack':
|
||||
if (file instanceof File) {
|
||||
this.dropzone.addFile.call(this.dropzone, file);
|
||||
} else {
|
||||
this.dropzone.files.push(file);
|
||||
this.dropzone.options.addedfile.call(this.dropzone, file);
|
||||
this.dropzone.options.thumbnail.call(this.dropzone, file, file.extras.url);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'removeFile':
|
||||
default:
|
||||
if (~this.dropzone.files.indexOf(file)) {
|
||||
file.rejected = true;
|
||||
this.dropzone.removeFile.call(this.dropzone, file, { silent: true });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
let modal = $('[data-remodal-id="generic"]');
|
||||
modal.find('.error-content').html(msg);
|
||||
$.remodal.lookup[modal.data('remodal')].open(); */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
export function UriToMarkdown(uri) {
|
||||
uri = uri.replace(/@3x|@2x|@1x/, '');
|
||||
uri = uri.replace(/\(/g, '%28');
|
||||
uri = uri.replace(/\)/g, '%29');
|
||||
|
||||
return uri.match(/\.(jpe?g|png|gif|svg)$/i) ? `` : `[${decodeURI(uri)}](${uri})`;
|
||||
}
|
||||
*/
|
||||
|
||||
let instances = [];
|
||||
let cache = $();
|
||||
const onAddedNodes = (event, target/* , record, instance */) => {
|
||||
let files = $(target).find('.dropzone.files-upload');
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
files.each((index, file) => {
|
||||
file = $(file);
|
||||
if (!~cache.index(file)) {
|
||||
addNode(file);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const addNode = (container) => {
|
||||
container = $(container);
|
||||
let input = container.find('input[type="file"]');
|
||||
let settings = container.data('grav-file-settings') || {};
|
||||
|
||||
if (settings.accept && ~settings.accept.indexOf('*')) {
|
||||
settings.accept = [''];
|
||||
}
|
||||
|
||||
let options = {
|
||||
url: container.data('file-url-add') || (container.closest('form').attr('action') || config.current_url) + '.json',
|
||||
paramName: settings.paramName || 'file',
|
||||
dotNotation: settings.name || 'file',
|
||||
acceptedFiles: settings.accept ? settings.accept.join(',') : input.attr('accept') || container.data('media-types'),
|
||||
maxFilesize: settings.filesize || 256,
|
||||
maxFiles: settings.limit || null,
|
||||
resizeWidth: settings.resizeWidth || null,
|
||||
resizeHeight: settings.resizeHeight || null,
|
||||
resizeQuality: settings.resizeQuality || null,
|
||||
accept: function(file, done) {
|
||||
const resolution = settings.resolution;
|
||||
let error = '';
|
||||
if (!resolution) return done();
|
||||
|
||||
if ((this.options.maxFiles != null) && (this.getAcceptedFiles().length >= this.options.maxFiles)) {
|
||||
done(this.options.dictMaxFilesExceeded.replace('{{maxFiles}}', this.options.maxFiles));
|
||||
return this.emit('maxfilesexceeded', file);
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
if (resolution.min || (!(settings.resizeWidth || settings.resizeHeight) && resolution.max)) {
|
||||
reader.onload = function(event) {
|
||||
const image = new Image();
|
||||
image.src = event.target.result;
|
||||
image.onload = function() {
|
||||
if (resolution.min) {
|
||||
Object.keys(resolution.min).forEach((attr) => {
|
||||
if (this[attr] < resolution.min[attr]) {
|
||||
error += translations.PLUGIN_FORM.RESOLUTION_MIN.replace(/{{attr}}/g, attr).replace(/{{min}}/g, resolution.min[attr]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!(settings.resizeWidth || settings.resizeHeight)) {
|
||||
if (resolution.max) {
|
||||
Object.keys(resolution.max).forEach((attr) => {
|
||||
if (this[attr] > resolution.max[attr]) {
|
||||
error += translations.PLUGIN_FORM.RESOLUTION_MAX.replace(/{{attr}}/g, attr).replace(/{{max}}/g, resolution.max[attr]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
done(error);
|
||||
};
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
return done(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cache = cache.add(container);
|
||||
container = container[0];
|
||||
instances.push(new FilesField({container, options}));
|
||||
};
|
||||
|
||||
export let Instances = (() => {
|
||||
$(document).ready(() => {
|
||||
$('.dropzone.files-upload').each((i, container) => addNode(container));
|
||||
$('body').on('mutation._grav', onAddedNodes);
|
||||
});
|
||||
|
||||
return instances;
|
||||
})();
|
||||
94
user/plugins/form/app/fields/form.js
Normal file
94
user/plugins/form/app/fields/form.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
const attachToggleables = (form) => {
|
||||
form = $(form);
|
||||
let query = '[data-grav-field="toggleable"] input[type="checkbox"]';
|
||||
|
||||
form.on('change', query, (event) => {
|
||||
let toggle = $(event.target);
|
||||
let enabled = toggle.is(':checked');
|
||||
let parent = toggle.closest('.form-field');
|
||||
let label = parent.find('label.toggleable');
|
||||
let fields = parent.find('.form-data');
|
||||
let inputs = fields.find('input, select, textarea, button');
|
||||
|
||||
label.add(fields).css('opacity', enabled ? '' : 0.7);
|
||||
inputs.map((index, input) => {
|
||||
let isSelectize = input.selectize;
|
||||
input = $(input);
|
||||
|
||||
if (isSelectize) {
|
||||
isSelectize[enabled ? 'enable' : 'disable']();
|
||||
} else {
|
||||
input.prop('disabled', !enabled);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
form.find(query).trigger('change');
|
||||
};
|
||||
|
||||
const attachDisabledFields = (form) => {
|
||||
form = $(form);
|
||||
let prefix = '.form-field-toggleable .form-data';
|
||||
let query = [];
|
||||
|
||||
['input', 'select', 'label[for]', 'textarea', '.selectize-control'].forEach((item) => {
|
||||
query.push(`${prefix} ${item}`);
|
||||
});
|
||||
|
||||
form.on('mousedown', query.join(', '), (event) => {
|
||||
let input = $(event.target);
|
||||
let isFor = input.prop('for');
|
||||
let isSelectize = (input.hasClass('selectize-control') || input.parents('.selectize-control')).length;
|
||||
|
||||
if (isFor) { input = $(`[id="${isFor}"]`); }
|
||||
if (isSelectize) { input = input.closest('.selectize-control').siblings('select[name]'); }
|
||||
|
||||
if (!input.prop('disabled')) { return true; }
|
||||
|
||||
let toggle = input.closest('.form-field').find('[data-grav-field="toggleable"] input[type="checkbox"]');
|
||||
toggle.trigger('click');
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
const submitUncheckedFields = (forms) => {
|
||||
forms = $(forms);
|
||||
let submitted = false;
|
||||
forms.each((index, form) => {
|
||||
form = $(form);
|
||||
form.on('submit', () => {
|
||||
// workaround for MS Edge, submitting multiple forms at the same time
|
||||
if (submitted) { return false; }
|
||||
|
||||
let formId = form.attr('id');
|
||||
let unchecked = form.find('input[type="checkbox"]:not(:checked):not(:disabled)');
|
||||
let submit = form.find('[type="submit"]').add(`[form="${formId}"][type="submit"]`);
|
||||
|
||||
if (!unchecked.length) { return true; }
|
||||
|
||||
submit.addClass('pointer-events-disabled');
|
||||
unchecked.each((index, element) => {
|
||||
element = $(element);
|
||||
let name = element.prop('name');
|
||||
let fake = $(`<input type="hidden" name="${name}" value="0" />`);
|
||||
form.append(fake);
|
||||
});
|
||||
submitted = true;
|
||||
return true;
|
||||
});
|
||||
});
|
||||
};
|
||||
*/
|
||||
|
||||
$(document).ready(() => {
|
||||
const forms = $('form').filter((form) => $(form).find('[name="__form-name__"]'));
|
||||
if (!forms.length) { return; }
|
||||
|
||||
forms.each((index, form) => {
|
||||
attachToggleables(form);
|
||||
attachDisabledFields(form);
|
||||
// submitUncheckedFields(form);
|
||||
});
|
||||
});
|
||||
7
user/plugins/form/app/fields/index.js
Normal file
7
user/plugins/form/app/fields/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import './form';
|
||||
import FileInstances from './file';
|
||||
import ArrayInstances from './array';
|
||||
import PageMedia, { Instance as PageMediaInstances } from './media';
|
||||
import './tabs';
|
||||
|
||||
export default { FileInstances, ArrayInstances, Media: { PageMedia, PageMediaInstances } };
|
||||
174
user/plugins/form/app/fields/media.js
Normal file
174
user/plugins/form/app/fields/media.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import $ from 'jquery';
|
||||
import FilesField from './file';
|
||||
import { config, translations } from 'grav-form';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
const template = `
|
||||
<div class="dz-preview dz-file-preview">
|
||||
<div class="dz-details">
|
||||
<div class="dz-filename"><span data-dz-name></span></div>
|
||||
<div class="dz-size" data-dz-size></div>
|
||||
<img data-dz-thumbnail />
|
||||
</div>
|
||||
<div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
|
||||
<div class="dz-success-mark"><span>✔</span></div>
|
||||
<div class="dz-error-mark"><span>✘</span></div>
|
||||
<div class="dz-error-message"><span data-dz-errormessage></span></div>
|
||||
<a class="dz-remove" title="${translations.PLUGIN_FORM.DELETE}" href="javascript:undefined;" data-dz-remove>${translations.PLUGIN_FORM.DELETE}</a>
|
||||
</div>`.trim();
|
||||
|
||||
export default class PageMedia extends FilesField {
|
||||
constructor({ container = '#grav-dropzone', options = {} } = {}) {
|
||||
const previewTemplate = $('#dropzone-media-template').html() || template;
|
||||
options = Object.assign(options, { previewTemplate });
|
||||
super({ container, options });
|
||||
if (!this.container.length) { return; }
|
||||
|
||||
this.urls = {
|
||||
fetch: `${this.container.data('media-url')}/task${config.param_sep}listmedia`,
|
||||
add: `${this.container.data('media-url')}/task${config.param_sep}addmedia`,
|
||||
delete: `${this.container.data('media-url')}/task${config.param_sep}delmedia`
|
||||
};
|
||||
|
||||
this.dropzone.options.url = this.urls.add;
|
||||
|
||||
if (typeof this.options.fetchMedia === 'undefined' || this.options.fetchMedia) {
|
||||
this.fetchMedia();
|
||||
}
|
||||
|
||||
const field = $(`[name="${this.container.data('dropzone-field')}"]`);
|
||||
|
||||
if (field.length) {
|
||||
this.sortable = new Sortable(this.container.get(0), {
|
||||
animation: 150,
|
||||
// forceFallback: true,
|
||||
setData: (dataTransfer, target) => {
|
||||
target = $(target);
|
||||
this.dropzone.disable();
|
||||
target.addClass('hide-backface');
|
||||
dataTransfer.effectAllowed = 'copy';
|
||||
},
|
||||
onSort: () => {
|
||||
let names = [];
|
||||
this.container.find('[data-dz-name]').each((index, file) => {
|
||||
file = $(file);
|
||||
const name = file.text().trim();
|
||||
names.push(name);
|
||||
});
|
||||
|
||||
field.val(names.join(','));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDropzoneRemovedFile(file, ...extra) {
|
||||
if (!file.accepted || file.rejected) { return; }
|
||||
const form = this.container.closest('form');
|
||||
const unique_id = form.find('[name="__unique_form_id__"]');
|
||||
let url = file.removeUrl || this.urls.delete || `${location.href}.json`;
|
||||
let path = (url || '').match(/path:(.*)\//);
|
||||
let data = new FormData();
|
||||
|
||||
data.append('filename', file.name);
|
||||
data.append('__form-name__', form.find('[name="__form-name__"]').val());
|
||||
if (unique_id.length) {
|
||||
data.append('__unique_form_id__', unique_id.val());
|
||||
}
|
||||
data.append('name', this.options.dotNotation);
|
||||
data.append('form-nonce', config.form_nonce);
|
||||
|
||||
if (file.sessionParams) {
|
||||
data.append('__form-file-remover__', '1');
|
||||
data.append('session', file.sessionParams);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url,
|
||||
data,
|
||||
method: 'POST',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: () => {
|
||||
if (!path) { return; }
|
||||
|
||||
path = global.atob(path[1]);
|
||||
let input = this.container.find('[name][type="hidden"]');
|
||||
let data = JSON.parse(input.val() || '{}');
|
||||
delete data[path];
|
||||
input.val(JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchMedia() {
|
||||
const order = this.container.closest('.form-field').find('[name="data[header][media_order]"]').val();
|
||||
const data = { order };
|
||||
let url = this.urls.fetch;
|
||||
|
||||
$.ajax({
|
||||
url,
|
||||
method: 'POST',
|
||||
data,
|
||||
success: (response) => {
|
||||
if (typeof response === 'string' || response instanceof String) {
|
||||
return false;
|
||||
}
|
||||
|
||||
response = response.results;
|
||||
Object.keys(response).forEach((name) => {
|
||||
let data = response[name];
|
||||
let mock = { name, size: data.size, accepted: true, extras: data };
|
||||
|
||||
this.dropzone.files.push(mock);
|
||||
this.dropzone.options.addedfile.call(this.dropzone, mock);
|
||||
this.dropzone.options.thumbnail.call(this.dropzone, mock, data.url);
|
||||
});
|
||||
|
||||
this.container.find('.dz-preview').prop('draggable', 'true');
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
request(url, { method: 'post', body }, (response) => {
|
||||
let results = response.results;
|
||||
|
||||
Object.keys(results).forEach((name) => {
|
||||
let data = results[name];
|
||||
let mock = { name, size: data.size, accepted: true, extras: data };
|
||||
|
||||
this.dropzone.files.push(mock);
|
||||
this.dropzone.options.addedfile.call(this.dropzone, mock);
|
||||
this.dropzone.options.thumbnail.call(this.dropzone, mock, data.url);
|
||||
});
|
||||
|
||||
this.container.find('.dz-preview').prop('draggable', 'true');
|
||||
});*/
|
||||
}
|
||||
|
||||
onDropzoneSending(file, xhr, formData) {
|
||||
/*
|
||||
// Cannot call super because Safari and IE API don't implement `delete`
|
||||
super.onDropzoneSending(file, xhr, formData);
|
||||
formData.delete('task');
|
||||
*/
|
||||
|
||||
formData.append('name', this.options.dotNotation);
|
||||
formData.append('admin-nonce', config.admin_nonce);
|
||||
}
|
||||
|
||||
onDropzoneComplete(file) {
|
||||
super.onDropzoneComplete(file);
|
||||
this.sortable.options.onSort();
|
||||
|
||||
// accepted
|
||||
$('.dz-preview').prop('draggable', 'true');
|
||||
}
|
||||
|
||||
// onDropzoneRemovedFile(file, ...extra) {
|
||||
// super.onDropzoneRemovedFile(file, ...extra);
|
||||
// this.sortable.options.onSort();
|
||||
// }
|
||||
}
|
||||
|
||||
export let Instance = new PageMedia();
|
||||
14
user/plugins/form/app/fields/tabs.js
Normal file
14
user/plugins/form/app/fields/tabs.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
$('body').on('touchstart click', '[data-tabid]', (event) => {
|
||||
event && event.stopPropagation();
|
||||
let target = $(event.currentTarget);
|
||||
|
||||
const panel = $(`[id="${target.data('tabid')}"]`);
|
||||
|
||||
target.siblings('[data-tabid]').removeClass('active');
|
||||
target.addClass('active');
|
||||
|
||||
panel.siblings('[id]').removeClass('active');
|
||||
panel.addClass('active');
|
||||
});
|
||||
4
user/plugins/form/app/main.js
Normal file
4
user/plugins/form/app/main.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import Instances from './fields';
|
||||
import './utils/keep-alive';
|
||||
|
||||
export { Instances };
|
||||
17
user/plugins/form/app/utils/keep-alive.js
Normal file
17
user/plugins/form/app/utils/keep-alive.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import $ from 'jquery';
|
||||
import {config} from 'grav-form';
|
||||
|
||||
const MAX_SAFE_DELAY = 2147483647;
|
||||
|
||||
$(document).ready(() => {
|
||||
const keepAlive = $('[data-grav-keepalive]');
|
||||
|
||||
if (keepAlive.length) {
|
||||
const timeout = config.session_timeout / 1.5 * 1000;
|
||||
setInterval(() => {
|
||||
$.ajax({
|
||||
url: `${config.base_url_relative}/task${config.param_sep}keep-alive`
|
||||
});
|
||||
}, Math.min(timeout, MAX_SAFE_DELAY));
|
||||
}
|
||||
});
|
||||
1
user/plugins/form/assets/dropzone.min.css
vendored
Normal file
1
user/plugins/form/assets/dropzone.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
user/plugins/form/assets/form-styles.css
Normal file
1
user/plugins/form/assets/form-styles.css
Normal file
@@ -0,0 +1 @@
|
||||
.form-group.has-errors{background:rgba(255,0,0,.05);border:1px solid rgba(255,0,0,.2);border-radius:3px;margin:0 -5px;padding:0 5px}.form-errors{color:#b52b27}.form-honeybear{visibility:hidden;position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}.form-errors p{margin:0}.form-input-file input{display:none}.form-input-file .dz-default.dz-message{position:absolute;text-align:center;left:0;right:0;top:50%;transform:translateY(-50%);margin:0}.form-input-file.dropzone{position:relative;min-height:70px;border-radius:3px;margin-bottom:.85rem;border:2px dashed #ccc;color:#aaa;padding:.5rem}.form-input-file.dropzone .dz-preview{margin:.5rem}.form-input-file.dropzone .dz-preview:hover{z-index:2}.form-input-file.dropzone .dz-preview .dz-error-message{min-width:140px;width:auto}.form-input-file.dropzone .dz-preview .dz-image,.form-input-file.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:3px;z-index:1}.form-tabs .tabs-nav{display:flex;padding-top:1px;margin-bottom:-1px}.form-tabs .tabs-nav a{flex:1;transition:color .5s ease,background .5s ease;cursor:pointer;text-align:center;padding:10px;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #ccc;border-radius:5px 5px 0 0}.form-tabs .tabs-nav a.active{border:1px solid #ccc;border-bottom:1px solid rgba(0,0,0,0);margin:0 -1px}.form-tabs .tabs-nav a.active span{color:#000}.form-tabs .tabs-nav span{display:inline-block;line-height:1.1}.form-tabs.subtle .tabs-nav{margin-right:0 !important}.form-tabs .tabs-content .tab__content{display:none;padding-top:2rem}.form-tabs .tabs-content .tab__content.active{display:block}.checkboxes{display:inline-block}.checkboxes label{display:inline;cursor:pointer;position:relative;padding:0 0 0 20px;margin-right:15px}.checkboxes label:before{content:"";display:inline-block;width:20px;height:20px;left:0;margin-top:0;margin-right:10px;position:absolute;border-radius:3px;border:1px solid #e6e6e6}.checkboxes input[type=checkbox]{display:none}.checkboxes input[type=checkbox]:checked+label:before{content:"✓";font-size:20px;line-height:1;text-align:center}.checkboxes.toggleable label{margin-right:0}.form-field-toggleable .checkboxes.toggleable{margin-right:5px;vertical-align:middle}.form-field-toggleable .checkboxes+label{display:inline-block}.switch-toggle{display:inline-flex;overflow:hidden;border-radius:3px;line-height:35px;border:1px solid #ccc}.switch-toggle input[type=radio]{position:absolute;visibility:hidden;display:none}.switch-toggle label{display:inline-block;cursor:pointer;padding:0 15px;margin:0;white-space:nowrap;color:inherit;transition:background-color .5s ease}.switch-toggle input.highlight:checked+label{background:#333;color:#fff}.switch-toggle input:checked+label{color:#fff;background:#999}.signature-pad{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-size:10px;width:100%;height:100%;max-width:700px;max-height:460px;border:1px solid #f0f0f0;background-color:#fff;padding:16px}.signature-pad--body{position:relative;-webkit-box-flex:1;-ms-flex:1;flex:1;border:1px solid #f6f6f6;min-height:100px}.signature-pad--body canvas{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:4px;box-shadow:0 0 5px rgba(0,0,0,.02) inset}.signature-pad--footer{color:#c3c3c3;text-align:center;font-size:1.2em}.signature-pad--actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin-top:8px}[data-grav-field=array] .form-row{display:flex;align-items:center;margin-bottom:.5rem}[data-grav-field=array] .form-row>input,[data-grav-field=array] .form-row>textarea{margin:0 .5rem;display:inline-block}.form-data.basic-captcha .form-input-wrapper{border:1px solid #ccc;border-radius:5px;display:flex;overflow:hidden}.form-data.basic-captcha .form-input-prepend{display:flex;color:#333;background-color:#ccc;flex-shrink:0}.form-data.basic-captcha .form-input-prepend img{margin:0}.form-data.basic-captcha .form-input-prepend button>svg{margin:0 8px;width:18px;height:18px}.form-data.basic-captcha input.form-input{border:0}/*# sourceMappingURL=form-styles.css.map */
|
||||
1
user/plugins/form/assets/form-styles.css.map
Normal file
1
user/plugins/form/assets/form-styles.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../scss/form-styles.scss"],"names":[],"mappings":"CAGA,uBACI,6BACA,kCACA,kBACA,cACA,cAGJ,aACI,cAGJ,gBACI,kBACA,6BACA,WACA,UACA,gBACA,8BAGJ,eACI,SAKA,uBACI,aAGJ,wCACI,kBACA,kBACA,OACA,QACA,QACA,2BACA,SAGJ,0BACI,kBACA,gBACA,kBACA,qBACA,uBACA,WACA,cAEA,sCACI,aAEA,4CACI,UAGJ,wDACI,gBACA,WAGJ,gHAEI,kBACA,UAWZ,qBACI,aACA,gBAEA,mBAEA,uBACI,OACA,8CACA,eACA,kBACA,aACA,aACA,mBACA,uBACA,6BACA,0BAEA,8BACI,sBACA,sCACA,cAEA,mCACI,MAtGA,KA2GZ,0BACI,qBACA,gBAKR,4BACI,0BAKA,uCACI,aACA,iBAEA,8CACI,cAOhB,YACI,qBAEA,kBACI,eACA,eACA,kBACA,mBACA,kBAGJ,yBACI,WACA,qBACA,WACA,YACA,OACA,aACA,kBACA,kBACA,kBAEA,yBAGJ,iCACI,aAEJ,sDACI,YACA,eACA,cACA,kBAGJ,6BACI,eAMJ,8CACI,iBACA,sBAEJ,yCACI,qBAKR,eACI,oBACA,gBACA,kBACA,iBACA,sBAEA,iCACI,kBACA,kBACA,aAGJ,qBACI,qBACA,eACA,eACA,SACA,mBACA,cACA,qCAGJ,6CACI,gBACA,WAGJ,mCACI,WACA,gBAOR,eACI,kBACA,oBACA,oBACA,aACA,4BACA,6BACA,0BACA,sBACA,eACA,WACA,YACA,gBACA,iBACA,yBACA,sBACA,aAGJ,qBACI,kBACA,mBACA,WACA,OACA,yBACA,iBAGJ,4BACI,kBACA,OACA,MACA,WACA,YACA,kBACA,yCAGJ,uBACI,cACA,kBACA,gBAGJ,wBACI,oBACA,oBACA,aACA,yBACA,sBACA,8BACA,eAGJ,kCACI,aACA,mBACA,oBAGJ,mFAGI,eACA,qBAIA,6CACI,sBACA,kBACA,aACA,gBAEJ,6CACI,aACA,WACA,sBACA,cACA,iDACI,SAEJ,wDACI,aACA,WACA,YAGR,0CACI","file":"form-styles.css"}
|
||||
1
user/plugins/form/assets/form-styles.min.css
vendored
Normal file
1
user/plugins/form/assets/form-styles.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.form-group.has-errors{margin:0 -5px;padding:0 5px;border:1px solid rgba(255,0,0,.2);border-radius:3px;background:rgba(255,0,0,.05)}.form-errors{color:#b52b27}.form-honeybear{position:absolute!important;visibility:hidden;overflow:hidden;clip:rect(1px,1px,1px,1px);width:1px;height:1px}.form-errors p{margin:0}.form-input-file input{display:none}.form-input-file .dz-default.dz-message{position:absolute;top:50%;right:0;left:0;margin:0;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center}.form-input-file.dropzone{position:relative;min-height:70px;margin-bottom:.85rem;padding:.5rem;color:#aaa;border:2px dashed #ccc;border-radius:3px}.form-input-file.dropzone .dz-preview{margin:.5rem}.form-input-file.dropzone .dz-preview:hover{z-index:2}.form-input-file.dropzone .dz-preview .dz-error-message{width:auto;min-width:140px}.form-input-file.dropzone .dz-preview .dz-image,.form-input-file.dropzone .dz-preview.dz-file-preview .dz-image{z-index:1;border-radius:3px}.form-tabs .tabs-nav{display:flex;margin-bottom:-1px;padding-top:1px}.form-tabs .tabs-nav a{display:flex;padding:10px;cursor:pointer;transition:color .5s ease,background .5s ease;text-align:center;border-bottom:1px solid #eee;border-radius:5px 5px 0 0;flex:1;align-items:center;justify-content:center}.form-tabs .tabs-nav a.active{margin:0 -1px;border:1px solid #eee;border-bottom:1px solid transparent}.form-tabs .tabs-nav a.active span{color:#000}.form-tabs .tabs-nav span{line-height:1.1;display:inline-block}.form-tabs.subtle .tabs-nav{margin-right:0!important}.form-tabs .tabs-content .tab__content{display:none;padding-top:2rem}.form-tabs .tabs-content .tab__content.active{display:block}.checkboxes{display:inline-block}.checkboxes label{position:relative;display:inline;margin-right:15px;padding:0 0 0 20px;cursor:pointer}.checkboxes label:before{position:absolute;left:0;display:inline-block;width:20px;height:20px;margin-top:0;margin-right:10px;content:'';border:1px solid #e6e6e6;border-radius:3px}.checkboxes input[type=checkbox]{display:none}.checkboxes input[type=checkbox]:checked+label:before{font-size:20px;line-height:1;content:'\2713';text-align:center}.checkboxes.toggleable label{margin-right:0}.form-field-toggleable .checkboxes.toggleable{margin-right:5px;vertical-align:middle}.form-field-toggleable .checkboxes+label{display:inline-block}.switch-toggle{line-height:35px;display:inline-flex;overflow:hidden;border:1px solid #eee;border-radius:3px}.switch-toggle input[type=radio]{position:absolute;display:none;visibility:hidden}.switch-toggle label{display:inline-block;margin:0;padding:0 15px;cursor:pointer;transition:background-color .5s ease;white-space:nowrap;color:inherit}.switch-toggle input.highlight:checked+label{color:#fff;background:#333}.switch-toggle input:checked+label{color:#fff;background:#999}.signature-pad{font-size:10px;position:relative;display:flex;flex-direction:column;width:100%;max-width:700px;height:100%;max-height:460px;padding:16px;border:1px solid #f0f0f0;background-color:#fff}.signature-pad--body{position:relative;min-height:100px;border:1px solid #f6f6f6;flex:1}.signature-pad--body canvas{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:4px;box-shadow:0 0 5px rgba(0,0,0,.02) inset}.signature-pad--footer{font-size:1.2em;text-align:center;color:#c3c3c3}.signature-pad--actions{display:flex;margin-top:8px;justify-content:space-between}[data-grav-field=array] .form-row{display:flex;margin-bottom:.5rem;align-items:center}[data-grav-field=array] .form-row>input,[data-grav-field=array] .form-row>textarea{display:inline-block;margin:0 .5rem}
|
||||
41
user/plugins/form/assets/form.min.js
vendored
Normal file
41
user/plugins/form/assets/form.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
24
user/plugins/form/assets/form.vendor.js
Normal file
24
user/plugins/form/assets/form.vendor.js
Normal file
File diff suppressed because one or more lines are too long
29
user/plugins/form/assets/object.assign.polyfill.js
Normal file
29
user/plugins/form/assets/object.assign.polyfill.js
Normal file
@@ -0,0 +1,29 @@
|
||||
if (typeof Object.assign !== 'function') {
|
||||
// Must be writable: true, enumerable: false, configurable: true
|
||||
Object.defineProperty(Object, 'assign', {
|
||||
value: function assign(target, varArgs) { // .length of function is 2
|
||||
'use strict';
|
||||
if (target == null) { // TypeError if undefined or null
|
||||
throw new TypeError('Cannot convert undefined or null to object');
|
||||
}
|
||||
|
||||
var to = Object(target);
|
||||
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var nextSource = arguments[index];
|
||||
|
||||
if (nextSource != null) { // Skip over if undefined or null
|
||||
for (var nextKey in nextSource) {
|
||||
// Avoid bugs when hasOwnProperty is shadowed
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
},
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
617
user/plugins/form/assets/signature_pad.js
Normal file
617
user/plugins/form/assets/signature_pad.js
Normal file
@@ -0,0 +1,617 @@
|
||||
/*!
|
||||
* Signature Pad v2.3.2
|
||||
* https://github.com/szimek/signature_pad
|
||||
*
|
||||
* Copyright 2017 Szymon Nowak
|
||||
* Released under the MIT license
|
||||
*
|
||||
* The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:
|
||||
* http://corner.squareup.com/2012/07/smoother-signatures.html
|
||||
*
|
||||
* Implementation of interpolation using cubic Bézier curves is taken from:
|
||||
* http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript
|
||||
*
|
||||
* Algorithm for approximated length of a Bézier curve is taken from:
|
||||
* http://www.lemoda.net/maths/bezier-length/index.html
|
||||
*
|
||||
*/
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.SignaturePad = factory());
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
function Point(x, y, time) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.time = time || new Date().getTime();
|
||||
}
|
||||
|
||||
Point.prototype.velocityFrom = function (start) {
|
||||
return this.time !== start.time ? this.distanceTo(start) / (this.time - start.time) : 1;
|
||||
};
|
||||
|
||||
Point.prototype.distanceTo = function (start) {
|
||||
return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
|
||||
};
|
||||
|
||||
Point.prototype.equals = function (other) {
|
||||
return this.x === other.x && this.y === other.y && this.time === other.time;
|
||||
};
|
||||
|
||||
function Bezier(startPoint, control1, control2, endPoint) {
|
||||
this.startPoint = startPoint;
|
||||
this.control1 = control1;
|
||||
this.control2 = control2;
|
||||
this.endPoint = endPoint;
|
||||
}
|
||||
|
||||
// Returns approximated length.
|
||||
Bezier.prototype.length = function () {
|
||||
var steps = 10;
|
||||
var length = 0;
|
||||
var px = void 0;
|
||||
var py = void 0;
|
||||
|
||||
for (var i = 0; i <= steps; i += 1) {
|
||||
var t = i / steps;
|
||||
var cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
|
||||
var cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
|
||||
if (i > 0) {
|
||||
var xdiff = cx - px;
|
||||
var ydiff = cy - py;
|
||||
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||||
}
|
||||
px = cx;
|
||||
py = cy;
|
||||
}
|
||||
|
||||
return length;
|
||||
};
|
||||
|
||||
/* eslint-disable no-multi-spaces, space-in-parens */
|
||||
Bezier.prototype._point = function (t, start, c1, c2, end) {
|
||||
return start * (1.0 - t) * (1.0 - t) * (1.0 - t) + 3.0 * c1 * (1.0 - t) * (1.0 - t) * t + 3.0 * c2 * (1.0 - t) * t * t + end * t * t * t;
|
||||
};
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// http://stackoverflow.com/a/27078401/815507
|
||||
function throttle(func, wait, options) {
|
||||
var context, args, result;
|
||||
var timeout = null;
|
||||
var previous = 0;
|
||||
if (!options) options = {};
|
||||
var later = function later() {
|
||||
previous = options.leading === false ? 0 : Date.now();
|
||||
timeout = null;
|
||||
result = func.apply(context, args);
|
||||
if (!timeout) context = args = null;
|
||||
};
|
||||
return function () {
|
||||
var now = Date.now();
|
||||
if (!previous && options.leading === false) previous = now;
|
||||
var remaining = wait - (now - previous);
|
||||
context = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0 || remaining > wait) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
previous = now;
|
||||
result = func.apply(context, args);
|
||||
if (!timeout) context = args = null;
|
||||
} else if (!timeout && options.trailing !== false) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function SignaturePad(canvas, options) {
|
||||
var self = this;
|
||||
var opts = options || {};
|
||||
|
||||
this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
|
||||
this.minWidth = opts.minWidth || 0.5;
|
||||
this.maxWidth = opts.maxWidth || 2.5;
|
||||
this.throttle = 'throttle' in opts ? opts.throttle : 16; // in miliseconds
|
||||
this.minDistance = 'minDistance' in opts ? opts.minDistance : 5;
|
||||
|
||||
if (this.throttle) {
|
||||
this._strokeMoveUpdate = throttle(SignaturePad.prototype._strokeUpdate, this.throttle);
|
||||
} else {
|
||||
this._strokeMoveUpdate = SignaturePad.prototype._strokeUpdate;
|
||||
}
|
||||
|
||||
this.dotSize = opts.dotSize || function () {
|
||||
return (this.minWidth + this.maxWidth) / 2;
|
||||
};
|
||||
this.penColor = opts.penColor || 'black';
|
||||
this.backgroundColor = opts.backgroundColor || 'rgba(0,0,0,0)';
|
||||
this.onBegin = opts.onBegin;
|
||||
this.onEnd = opts.onEnd;
|
||||
|
||||
this._canvas = canvas;
|
||||
this._ctx = canvas.getContext('2d');
|
||||
this.clear();
|
||||
|
||||
// We need add these inline so they are available to unbind while still having
|
||||
// access to 'self' we could use _.bind but it's not worth adding a dependency.
|
||||
this._handleMouseDown = function (event) {
|
||||
if (event.which === 1) {
|
||||
self._mouseButtonDown = true;
|
||||
self._strokeBegin(event);
|
||||
}
|
||||
};
|
||||
|
||||
this._handleMouseMove = function (event) {
|
||||
if (self._mouseButtonDown) {
|
||||
self._strokeMoveUpdate(event);
|
||||
}
|
||||
};
|
||||
|
||||
this._handleMouseUp = function (event) {
|
||||
if (event.which === 1 && self._mouseButtonDown) {
|
||||
self._mouseButtonDown = false;
|
||||
self._strokeEnd(event);
|
||||
}
|
||||
};
|
||||
|
||||
this._handleTouchStart = function (event) {
|
||||
// Prevent scrolling.
|
||||
event.preventDefault();
|
||||
|
||||
if (event.targetTouches.length === 1) {
|
||||
var touch = event.changedTouches[0];
|
||||
self._strokeBegin(touch);
|
||||
}
|
||||
};
|
||||
|
||||
this._handleTouchMove = function (event) {
|
||||
// Prevent scrolling.
|
||||
event.preventDefault();
|
||||
|
||||
var touch = event.targetTouches[0];
|
||||
self._strokeMoveUpdate(touch);
|
||||
};
|
||||
|
||||
this._handleTouchEnd = function (event) {
|
||||
var wasCanvasTouched = event.target === self._canvas;
|
||||
if (wasCanvasTouched) {
|
||||
event.preventDefault();
|
||||
self._strokeEnd(event);
|
||||
}
|
||||
};
|
||||
|
||||
// Enable mouse and touch event handlers
|
||||
this.on();
|
||||
}
|
||||
|
||||
// Public methods
|
||||
SignaturePad.prototype.clear = function () {
|
||||
var ctx = this._ctx;
|
||||
var canvas = this._canvas;
|
||||
|
||||
ctx.fillStyle = this.backgroundColor;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
this._data = [];
|
||||
this._reset();
|
||||
this._isEmpty = true;
|
||||
};
|
||||
|
||||
SignaturePad.prototype.fromDataURL = function (dataUrl) {
|
||||
var _this = this;
|
||||
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
var image = new Image();
|
||||
var ratio = options.ratio || window.devicePixelRatio || 1;
|
||||
var width = options.width || this._canvas.width / ratio;
|
||||
var height = options.height || this._canvas.height / ratio;
|
||||
|
||||
this._reset();
|
||||
image.src = dataUrl;
|
||||
image.onload = function () {
|
||||
_this._ctx.drawImage(image, 0, 0, width, height);
|
||||
};
|
||||
this._isEmpty = false;
|
||||
};
|
||||
|
||||
SignaturePad.prototype.toDataURL = function (type) {
|
||||
var _canvas;
|
||||
|
||||
switch (type) {
|
||||
case 'image/svg+xml':
|
||||
return this._toSVG();
|
||||
default:
|
||||
for (var _len = arguments.length, options = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||||
options[_key - 1] = arguments[_key];
|
||||
}
|
||||
|
||||
return (_canvas = this._canvas).toDataURL.apply(_canvas, [type].concat(options));
|
||||
}
|
||||
};
|
||||
|
||||
SignaturePad.prototype.on = function () {
|
||||
this._handleMouseEvents();
|
||||
this._handleTouchEvents();
|
||||
};
|
||||
|
||||
SignaturePad.prototype.off = function () {
|
||||
// Pass touch events to canvas element on mobile IE11 and Edge.
|
||||
this._canvas.style.msTouchAction = 'auto';
|
||||
this._canvas.style.touchAction = 'auto';
|
||||
|
||||
this._canvas.removeEventListener('mousedown', this._handleMouseDown);
|
||||
this._canvas.removeEventListener('mousemove', this._handleMouseMove);
|
||||
document.removeEventListener('mouseup', this._handleMouseUp);
|
||||
|
||||
this._canvas.removeEventListener('touchstart', this._handleTouchStart);
|
||||
this._canvas.removeEventListener('touchmove', this._handleTouchMove);
|
||||
this._canvas.removeEventListener('touchend', this._handleTouchEnd);
|
||||
};
|
||||
|
||||
SignaturePad.prototype.isEmpty = function () {
|
||||
return this._isEmpty;
|
||||
};
|
||||
|
||||
// Private methods
|
||||
SignaturePad.prototype._strokeBegin = function (event) {
|
||||
this._data.push([]);
|
||||
this._reset();
|
||||
this._strokeUpdate(event);
|
||||
|
||||
if (typeof this.onBegin === 'function') {
|
||||
this.onBegin(event);
|
||||
}
|
||||
};
|
||||
|
||||
SignaturePad.prototype._strokeUpdate = function (event) {
|
||||
var x = event.clientX;
|
||||
var y = event.clientY;
|
||||
|
||||
var point = this._createPoint(x, y);
|
||||
var lastPointGroup = this._data[this._data.length - 1];
|
||||
var lastPoint = lastPointGroup && lastPointGroup[lastPointGroup.length - 1];
|
||||
var isLastPointTooClose = lastPoint && point.distanceTo(lastPoint) < this.minDistance;
|
||||
|
||||
// Skip this point if it's too close to the previous one
|
||||
if (!(lastPoint && isLastPointTooClose)) {
|
||||
var _addPoint = this._addPoint(point),
|
||||
curve = _addPoint.curve,
|
||||
widths = _addPoint.widths;
|
||||
|
||||
if (curve && widths) {
|
||||
this._drawCurve(curve, widths.start, widths.end);
|
||||
}
|
||||
|
||||
this._data[this._data.length - 1].push({
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
time: point.time,
|
||||
color: this.penColor
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
SignaturePad.prototype._strokeEnd = function (event) {
|
||||
var canDrawCurve = this.points.length > 2;
|
||||
var point = this.points[0]; // Point instance
|
||||
|
||||
if (!canDrawCurve && point) {
|
||||
this._drawDot(point);
|
||||
}
|
||||
|
||||
if (point) {
|
||||
var lastPointGroup = this._data[this._data.length - 1];
|
||||
var lastPoint = lastPointGroup[lastPointGroup.length - 1]; // plain object
|
||||
|
||||
// When drawing a dot, there's only one point in a group, so without this check
|
||||
// such group would end up with exactly the same 2 points.
|
||||
if (!point.equals(lastPoint)) {
|
||||
lastPointGroup.push({
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
time: point.time,
|
||||
color: this.penColor
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.onEnd === 'function') {
|
||||
this.onEnd(event);
|
||||
}
|
||||
};
|
||||
|
||||
SignaturePad.prototype._handleMouseEvents = function () {
|
||||
this._mouseButtonDown = false;
|
||||
|
||||
this._canvas.addEventListener('mousedown', this._handleMouseDown);
|
||||
this._canvas.addEventListener('mousemove', this._handleMouseMove);
|
||||
document.addEventListener('mouseup', this._handleMouseUp);
|
||||
};
|
||||
|
||||
SignaturePad.prototype._handleTouchEvents = function () {
|
||||
// Pass touch events to canvas element on mobile IE11 and Edge.
|
||||
this._canvas.style.msTouchAction = 'none';
|
||||
this._canvas.style.touchAction = 'none';
|
||||
|
||||
this._canvas.addEventListener('touchstart', this._handleTouchStart);
|
||||
this._canvas.addEventListener('touchmove', this._handleTouchMove);
|
||||
this._canvas.addEventListener('touchend', this._handleTouchEnd);
|
||||
};
|
||||
|
||||
SignaturePad.prototype._reset = function () {
|
||||
this.points = [];
|
||||
this._lastVelocity = 0;
|
||||
this._lastWidth = (this.minWidth + this.maxWidth) / 2;
|
||||
this._ctx.fillStyle = this.penColor;
|
||||
};
|
||||
|
||||
SignaturePad.prototype._createPoint = function (x, y, time) {
|
||||
var rect = this._canvas.getBoundingClientRect();
|
||||
|
||||
return new Point(x - rect.left, y - rect.top, time || new Date().getTime());
|
||||
};
|
||||
|
||||
SignaturePad.prototype._addPoint = function (point) {
|
||||
var points = this.points;
|
||||
var tmp = void 0;
|
||||
|
||||
points.push(point);
|
||||
|
||||
if (points.length > 2) {
|
||||
// To reduce the initial lag make it work with 3 points
|
||||
// by copying the first point to the beginning.
|
||||
if (points.length === 3) points.unshift(points[0]);
|
||||
|
||||
tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
|
||||
var c2 = tmp.c2;
|
||||
tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
|
||||
var c3 = tmp.c1;
|
||||
var curve = new Bezier(points[1], c2, c3, points[2]);
|
||||
var widths = this._calculateCurveWidths(curve);
|
||||
|
||||
// Remove the first element from the list,
|
||||
// so that we always have no more than 4 points in points array.
|
||||
points.shift();
|
||||
|
||||
return { curve: curve, widths: widths };
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
|
||||
var dx1 = s1.x - s2.x;
|
||||
var dy1 = s1.y - s2.y;
|
||||
var dx2 = s2.x - s3.x;
|
||||
var dy2 = s2.y - s3.y;
|
||||
|
||||
var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
|
||||
var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
|
||||
|
||||
var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
|
||||
var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
||||
|
||||
var dxm = m1.x - m2.x;
|
||||
var dym = m1.y - m2.y;
|
||||
|
||||
var k = l2 / (l1 + l2);
|
||||
var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
|
||||
|
||||
var tx = s2.x - cm.x;
|
||||
var ty = s2.y - cm.y;
|
||||
|
||||
return {
|
||||
c1: new Point(m1.x + tx, m1.y + ty),
|
||||
c2: new Point(m2.x + tx, m2.y + ty)
|
||||
};
|
||||
};
|
||||
|
||||
SignaturePad.prototype._calculateCurveWidths = function (curve) {
|
||||
var startPoint = curve.startPoint;
|
||||
var endPoint = curve.endPoint;
|
||||
var widths = { start: null, end: null };
|
||||
|
||||
var velocity = this.velocityFilterWeight * endPoint.velocityFrom(startPoint) + (1 - this.velocityFilterWeight) * this._lastVelocity;
|
||||
|
||||
var newWidth = this._strokeWidth(velocity);
|
||||
|
||||
widths.start = this._lastWidth;
|
||||
widths.end = newWidth;
|
||||
|
||||
this._lastVelocity = velocity;
|
||||
this._lastWidth = newWidth;
|
||||
|
||||
return widths;
|
||||
};
|
||||
|
||||
SignaturePad.prototype._strokeWidth = function (velocity) {
|
||||
return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
|
||||
};
|
||||
|
||||
SignaturePad.prototype._drawPoint = function (x, y, size) {
|
||||
var ctx = this._ctx;
|
||||
|
||||
ctx.moveTo(x, y);
|
||||
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
|
||||
this._isEmpty = false;
|
||||
};
|
||||
|
||||
SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
|
||||
var ctx = this._ctx;
|
||||
var widthDelta = endWidth - startWidth;
|
||||
var drawSteps = Math.floor(curve.length());
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
for (var i = 0; i < drawSteps; i += 1) {
|
||||
// Calculate the Bezier (x, y) coordinate for this step.
|
||||
var t = i / drawSteps;
|
||||
var tt = t * t;
|
||||
var ttt = tt * t;
|
||||
var u = 1 - t;
|
||||
var uu = u * u;
|
||||
var uuu = uu * u;
|
||||
|
||||
var x = uuu * curve.startPoint.x;
|
||||
x += 3 * uu * t * curve.control1.x;
|
||||
x += 3 * u * tt * curve.control2.x;
|
||||
x += ttt * curve.endPoint.x;
|
||||
|
||||
var y = uuu * curve.startPoint.y;
|
||||
y += 3 * uu * t * curve.control1.y;
|
||||
y += 3 * u * tt * curve.control2.y;
|
||||
y += ttt * curve.endPoint.y;
|
||||
|
||||
var width = startWidth + ttt * widthDelta;
|
||||
this._drawPoint(x, y, width);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
SignaturePad.prototype._drawDot = function (point) {
|
||||
var ctx = this._ctx;
|
||||
var width = typeof this.dotSize === 'function' ? this.dotSize() : this.dotSize;
|
||||
|
||||
ctx.beginPath();
|
||||
this._drawPoint(point.x, point.y, width);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
SignaturePad.prototype._fromData = function (pointGroups, drawCurve, drawDot) {
|
||||
for (var i = 0; i < pointGroups.length; i += 1) {
|
||||
var group = pointGroups[i];
|
||||
|
||||
if (group.length > 1) {
|
||||
for (var j = 0; j < group.length; j += 1) {
|
||||
var rawPoint = group[j];
|
||||
var point = new Point(rawPoint.x, rawPoint.y, rawPoint.time);
|
||||
var color = rawPoint.color;
|
||||
|
||||
if (j === 0) {
|
||||
// First point in a group. Nothing to draw yet.
|
||||
|
||||
// All points in the group have the same color, so it's enough to set
|
||||
// penColor just at the beginning.
|
||||
this.penColor = color;
|
||||
this._reset();
|
||||
|
||||
this._addPoint(point);
|
||||
} else if (j !== group.length - 1) {
|
||||
// Middle point in a group.
|
||||
var _addPoint2 = this._addPoint(point),
|
||||
curve = _addPoint2.curve,
|
||||
widths = _addPoint2.widths;
|
||||
|
||||
if (curve && widths) {
|
||||
drawCurve(curve, widths, color);
|
||||
}
|
||||
} else {
|
||||
// Last point in a group. Do nothing.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._reset();
|
||||
var _rawPoint = group[0];
|
||||
drawDot(_rawPoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SignaturePad.prototype._toSVG = function () {
|
||||
var _this2 = this;
|
||||
|
||||
var pointGroups = this._data;
|
||||
var canvas = this._canvas;
|
||||
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||
var minX = 0;
|
||||
var minY = 0;
|
||||
var maxX = canvas.width / ratio;
|
||||
var maxY = canvas.height / ratio;
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
|
||||
svg.setAttributeNS(null, 'width', canvas.width);
|
||||
svg.setAttributeNS(null, 'height', canvas.height);
|
||||
|
||||
this._fromData(pointGroups, function (curve, widths, color) {
|
||||
var path = document.createElement('path');
|
||||
|
||||
// Need to check curve for NaN values, these pop up when drawing
|
||||
// lines on the canvas that are not continuous. E.g. Sharp corners
|
||||
// or stopping mid-stroke and than continuing without lifting mouse.
|
||||
if (!isNaN(curve.control1.x) && !isNaN(curve.control1.y) && !isNaN(curve.control2.x) && !isNaN(curve.control2.y)) {
|
||||
var attr = 'M ' + curve.startPoint.x.toFixed(3) + ',' + curve.startPoint.y.toFixed(3) + ' ' + ('C ' + curve.control1.x.toFixed(3) + ',' + curve.control1.y.toFixed(3) + ' ') + (curve.control2.x.toFixed(3) + ',' + curve.control2.y.toFixed(3) + ' ') + (curve.endPoint.x.toFixed(3) + ',' + curve.endPoint.y.toFixed(3));
|
||||
|
||||
path.setAttribute('d', attr);
|
||||
path.setAttribute('stroke-width', (widths.end * 2.25).toFixed(3));
|
||||
path.setAttribute('stroke', color);
|
||||
path.setAttribute('fill', 'none');
|
||||
path.setAttribute('stroke-linecap', 'round');
|
||||
|
||||
svg.appendChild(path);
|
||||
}
|
||||
}, function (rawPoint) {
|
||||
var circle = document.createElement('circle');
|
||||
var dotSize = typeof _this2.dotSize === 'function' ? _this2.dotSize() : _this2.dotSize;
|
||||
circle.setAttribute('r', dotSize);
|
||||
circle.setAttribute('cx', rawPoint.x);
|
||||
circle.setAttribute('cy', rawPoint.y);
|
||||
circle.setAttribute('fill', rawPoint.color);
|
||||
|
||||
svg.appendChild(circle);
|
||||
});
|
||||
|
||||
var prefix = 'data:image/svg+xml;base64,';
|
||||
var header = '<svg' + ' xmlns="http://www.w3.org/2000/svg"' + ' xmlns:xlink="http://www.w3.org/1999/xlink"' + (' viewBox="' + minX + ' ' + minY + ' ' + maxX + ' ' + maxY + '"') + (' width="' + maxX + '"') + (' height="' + maxY + '"') + '>';
|
||||
var body = svg.innerHTML;
|
||||
|
||||
// IE hack for missing innerHTML property on SVGElement
|
||||
if (body === undefined) {
|
||||
var dummy = document.createElement('dummy');
|
||||
var nodes = svg.childNodes;
|
||||
dummy.innerHTML = '';
|
||||
|
||||
for (var i = 0; i < nodes.length; i += 1) {
|
||||
dummy.appendChild(nodes[i].cloneNode(true));
|
||||
}
|
||||
|
||||
body = dummy.innerHTML;
|
||||
}
|
||||
|
||||
var footer = '</svg>';
|
||||
var data = header + body + footer;
|
||||
|
||||
return prefix + btoa(data);
|
||||
};
|
||||
|
||||
SignaturePad.prototype.fromData = function (pointGroups) {
|
||||
var _this3 = this;
|
||||
|
||||
this.clear();
|
||||
|
||||
this._fromData(pointGroups, function (curve, widths) {
|
||||
return _this3._drawCurve(curve, widths.start, widths.end);
|
||||
}, function (rawPoint) {
|
||||
return _this3._drawDot(rawPoint);
|
||||
});
|
||||
|
||||
this._data = pointGroups;
|
||||
};
|
||||
|
||||
SignaturePad.prototype.toData = function () {
|
||||
return this._data;
|
||||
};
|
||||
|
||||
return SignaturePad;
|
||||
|
||||
})));
|
||||
322
user/plugins/form/blueprints.yaml
Normal file
322
user/plugins/form/blueprints.yaml
Normal file
@@ -0,0 +1,322 @@
|
||||
name: Form
|
||||
slug: form
|
||||
type: plugin
|
||||
version: 7.0.1
|
||||
description: Enables forms handling and processing
|
||||
icon: check-square
|
||||
author:
|
||||
name: Team Grav
|
||||
email: devs@getgrav.org
|
||||
url: https://getgrav.org
|
||||
keywords: plugin, form
|
||||
homepage: https://github.com/getgrav/grav-plugin-form
|
||||
bugs: https://github.com/getgrav/grav-plugin-form/issues
|
||||
license: MIT
|
||||
|
||||
dependencies:
|
||||
- { name: grav, version: '>=1.7.24' }
|
||||
|
||||
form:
|
||||
validation: strict
|
||||
fields:
|
||||
enabled:
|
||||
type: hidden
|
||||
label: PLUGIN_ADMIN.PLUGIN_STATUS
|
||||
highlight: 1
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
general:
|
||||
type: section
|
||||
title: PLUGIN_FORM.GENERAL
|
||||
|
||||
fields:
|
||||
built_in_css:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.USE_BUILT_IN_CSS
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
inline_css:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.USE_INLINE_CSS
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
refresh_prevention:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.REFRESH_PREVENTION
|
||||
help: PLUGIN_FORM.REFRESH_PREVENTION_HELP
|
||||
highlight: 1
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
client_side_validation:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.CLIENT_SIDE_VALIDATION
|
||||
help: PLUGIN_FORM.CLIENT_SIDE_VALIDATION_HELP
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
inline_errors:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.INLINE_ERRORS
|
||||
help: PLUGIN_FORM.INLINE_ERRORS_HELP
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
files:
|
||||
type: section
|
||||
title: PLUGIN_FORM.FILES
|
||||
|
||||
fields:
|
||||
files.multiple:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.ALLOW_MULTIPLE
|
||||
help: PLUGIN_FORM.ALLOW_MULTIPLE_HELP
|
||||
highlight: 1
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
files.limit:
|
||||
type: text
|
||||
size: x-small
|
||||
label: PLUGIN_FORM.LIMIT
|
||||
help: PLUGIN_FORM.LIMIT_HELP
|
||||
default: 10
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
files.destination:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_FORM.DESTINATION
|
||||
help: PLUGIN_FORM.DESTINATION_HELP
|
||||
default: '@self'
|
||||
files.accept:
|
||||
type: selectize
|
||||
size: large
|
||||
label: PLUGIN_FORM.ACCEPT
|
||||
help: PLUGIN_FORM.ACCEPT_HELP
|
||||
classes: fancy
|
||||
default:
|
||||
- image/*
|
||||
validate:
|
||||
type: commalist
|
||||
files.filesize:
|
||||
type: text
|
||||
label: PLUGIN_FORM.FILESIZE
|
||||
help: PLUGIN_FORM.FILESIZE_HELP
|
||||
size: x-small
|
||||
default: 5
|
||||
validate:
|
||||
type: number
|
||||
min: 0
|
||||
files.avoid_overwriting:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.AVOID_OVERWRITING
|
||||
help: PLUGIN_FORM.AVOID_OVERWRITING_HELP
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
files.random_name:
|
||||
type: toggle
|
||||
label: PLUGIN_FORM.RANDOM_NAME
|
||||
help: PLUGIN_FORM.RANDOM_NAME_HELP
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
recaptcha:
|
||||
type: section
|
||||
title: PLUGIN_FORM.RECAPTCHA
|
||||
|
||||
fields:
|
||||
recaptcha.version:
|
||||
type: select
|
||||
label: PLUGIN_FORM.RECAPTCHA_VERSION
|
||||
default: 2-checkbox
|
||||
options:
|
||||
2-checkbox: PLUGIN_FORM.RECAPTCHA_VERSION_V2_CHECKBOX
|
||||
2-invisible: PLUGIN_FORM.RECAPTCHA_VERSION_V2_INVISIBLE
|
||||
3: PLUGIN_FORM.RECAPTCHA_VERSION_V3_LATEST
|
||||
recaptcha.theme:
|
||||
type: select
|
||||
label: PLUGIN_FORM.RECAPTCHA_THEME
|
||||
default: light
|
||||
options:
|
||||
light: PLUGIN_FORM.RECAPTCHA_THEME_LIGHT
|
||||
dark: PLUGIN_FORM.RECAPTCHA_THEME_DARK
|
||||
recaptcha.site_key:
|
||||
recaptcha.site_key:
|
||||
type: text
|
||||
label: PLUGIN_FORM.RECAPTCHA_SITE_KEY
|
||||
help: PLUGIN_FORM.RECAPTCHA_SITE_KEY_HELP
|
||||
default: ''
|
||||
recaptcha.secret_key:
|
||||
type: text
|
||||
label: PLUGIN_FORM.RECAPTCHA_SECRET_KEY
|
||||
help: PLUGIN_FORM.RECAPTCHA_SECRET_KEY_HELP
|
||||
default: ''
|
||||
|
||||
basic_captcha:
|
||||
type: section
|
||||
title: PLUGIN_FORM.BASIC_CAPTCHA
|
||||
|
||||
fields:
|
||||
basic_captcha.type:
|
||||
type: elements
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_TYPE
|
||||
default: 'characters'
|
||||
size: medium
|
||||
options:
|
||||
characters: Random Characters
|
||||
math: Math Puzzle
|
||||
fields:
|
||||
characters:
|
||||
type: element
|
||||
fields:
|
||||
basic_captcha.chars.length:
|
||||
type: range
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_LENGTH
|
||||
default: 6
|
||||
validate:
|
||||
min: 4
|
||||
max: 12
|
||||
append: characters
|
||||
basic_captcha.chars.font:
|
||||
type: select
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_FONT
|
||||
default: zxx-noise.ttf
|
||||
options:
|
||||
'zxx-noise.ttf': zxx-Noise
|
||||
'zxx-xed.ttf': zxx-Xed
|
||||
'zxx-camo.ttf': zxx-Camo
|
||||
'zxx-sans.ttf': zxx-Sans
|
||||
basic_captcha.chars.size:
|
||||
type: range
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_SIZE
|
||||
default: 24
|
||||
append: px
|
||||
validate:
|
||||
min: 12
|
||||
max: 32
|
||||
step: 2
|
||||
basic_captcha.chars.bg:
|
||||
type: colorpicker
|
||||
size: small
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_BG_COLOR
|
||||
default: '#ffffff'
|
||||
basic_captcha.chars.text:
|
||||
type: colorpicker
|
||||
size: small
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_TEXT_COLOR
|
||||
default: '#000000'
|
||||
basic_captcha.chars.start_x:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_START_X
|
||||
default: 5
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.chars.start_y:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_START_Y
|
||||
default: 30
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.chars.box_width:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_BOX_WIDTH
|
||||
default: 135
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.chars.box_height:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_BOX_HEIGHT
|
||||
default: 40
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
math:
|
||||
type: element
|
||||
fields:
|
||||
basic_captcha.math.min:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_MIN
|
||||
default: 1
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.math.max:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_MAX
|
||||
default: 10
|
||||
size: small
|
||||
validate:
|
||||
min: 1
|
||||
type: number
|
||||
basic_captcha.math.operators:
|
||||
type: selectize
|
||||
selectize:
|
||||
options:
|
||||
- value: '+'
|
||||
text: '+ Addition'
|
||||
- value: '-'
|
||||
text: '- Subtraction'
|
||||
- value: '*'
|
||||
text: 'x Multiplication'
|
||||
- value: '/'
|
||||
text: '/ Division'
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_OPERATORS
|
||||
validate:
|
||||
type: commalist
|
||||
122
user/plugins/form/classes/BasicCaptcha.php
Normal file
122
user/plugins/form/classes/BasicCaptcha.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\Form;
|
||||
|
||||
use GdImage;
|
||||
use Grav\Common\Grav;
|
||||
|
||||
class BasicCaptcha
|
||||
{
|
||||
protected $session = null;
|
||||
protected $key = 'basic_captcha_code';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->session = Grav::instance()['session'];
|
||||
}
|
||||
|
||||
public function getCaptchaCode($length = null): string
|
||||
{
|
||||
$config = Grav::instance()['config']->get('plugins.form.basic_captcha');
|
||||
$type = $config['type'] ?? 'characters';
|
||||
|
||||
if ($type == 'math') {
|
||||
$min = $config['math']['min'] ?? 1;
|
||||
$max = $config['math']['max'] ?? 12;
|
||||
$operators = $config['math']['operators'] ?? ['+','-','*'];
|
||||
|
||||
$first_num = random_int($min, $max);
|
||||
$second_num = random_int($min, $max);
|
||||
$operator = $operators[array_rand($operators)];
|
||||
|
||||
// calculator
|
||||
if ($operator === '-') {
|
||||
if ($first_num < $second_num) {
|
||||
$result = "$second_num-$first_num";
|
||||
$captcha_code = $second_num-$first_num;
|
||||
} else {
|
||||
$result = "$first_num-$second_num";
|
||||
$captcha_code = $first_num - $second_num;
|
||||
}
|
||||
} elseif ($operator === '*') {
|
||||
$result = "{$first_num}x{$second_num}";
|
||||
$captcha_code = $first_num - $second_num;
|
||||
} elseif ($operator === '/') {
|
||||
$result = "$first_num/ second_num";
|
||||
$captcha_code = $first_num / $second_num;
|
||||
} elseif ($operator === '+') {
|
||||
$result = "$first_num+$second_num";
|
||||
$captcha_code = $first_num + $second_num;
|
||||
}
|
||||
} else {
|
||||
if ($length === null) {
|
||||
$length = $config['chars']['length'] ?? 6;
|
||||
}
|
||||
$random_alpha = md5(random_bytes(64));
|
||||
$captcha_code = substr($random_alpha, 0, $length);
|
||||
$result = $captcha_code;
|
||||
}
|
||||
|
||||
|
||||
$this->setSession($this->key, $captcha_code);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setSession($key, $value): void
|
||||
{
|
||||
$this->session->$key = $value;
|
||||
}
|
||||
|
||||
public function getSession($key = null): ?string
|
||||
{
|
||||
if ($key === null) {
|
||||
$key = $this->key;
|
||||
}
|
||||
return $this->session->$key ?? null;
|
||||
}
|
||||
|
||||
public function createCaptchaImage($captcha_code)
|
||||
{
|
||||
$config = Grav::instance()['config']->get('plugins.form.basic_captcha');
|
||||
$font = $config['chars']['font'] ?? 'zxx-xed.ttf';
|
||||
|
||||
$target_layer = imagecreatetruecolor($config['chars']['box_width'], $config['chars']['box_height']);
|
||||
|
||||
$bg = $this->hexToRgb($config['chars']['bg'] ?? '#ffffff');
|
||||
$text = $this->hexToRgb($config['chars']['text'] ?? '#000000');
|
||||
|
||||
$captcha_background = imagecolorallocate($target_layer, $bg[0], $bg[1], $bg[2]);
|
||||
$captcha_text_color = imagecolorallocate($target_layer, $text[0], $text[1], $text[2]);
|
||||
|
||||
$font_path = __DIR__ . '/../fonts/' . $font;
|
||||
|
||||
imagefill($target_layer, 0, 0, $captcha_background);
|
||||
|
||||
imagefttext($target_layer, $config['chars']['size'], 0, $config['chars']['start_x'], $config['chars']['start_y'], $captcha_text_color, $font_path, $captcha_code);
|
||||
return $target_layer;
|
||||
}
|
||||
|
||||
public function renderCaptchaImage($imageData): void
|
||||
{
|
||||
header("Content-type: image/jpeg");
|
||||
imagejpeg($imageData);
|
||||
}
|
||||
|
||||
public function validateCaptcha($formData): bool
|
||||
{
|
||||
$isValid = false;
|
||||
$capchaSessionData = $this->getSession();
|
||||
|
||||
if ($capchaSessionData == $formData) {
|
||||
$isValid = true;
|
||||
}
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
private function hexToRgb($hex): array
|
||||
{
|
||||
return sscanf($hex, "#%02x%02x%02x");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1422
user/plugins/form/classes/Form.php
Normal file
1422
user/plugins/form/classes/Form.php
Normal file
File diff suppressed because it is too large
Load Diff
38
user/plugins/form/classes/FormFactory.php
Normal file
38
user/plugins/form/classes/FormFactory.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Grav\Plugin\Form;
|
||||
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Framework\Form\Interfaces\FormFactoryInterface;
|
||||
use Grav\Framework\Form\Interfaces\FormInterface;
|
||||
|
||||
class FormFactory implements FormFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Create form using the header of the page.
|
||||
*
|
||||
* @param Page $page
|
||||
* @param string $name
|
||||
* @param array $form
|
||||
* @return Form|null
|
||||
* @deprecated 1.6 Use FormFactory::createFormByPage() instead.
|
||||
*/
|
||||
public function createPageForm(Page $page, string $name, array $form): ?FormInterface
|
||||
{
|
||||
return new Form($page, $name, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create form using the header of the page.
|
||||
*
|
||||
* @param PageInterface $page
|
||||
* @param string $name
|
||||
* @param array $form
|
||||
* @return Form|null
|
||||
*/
|
||||
public function createFormForPage(PageInterface $page, string $name, array $form): ?FormInterface
|
||||
{
|
||||
return new Form($page, $name, $form);
|
||||
}
|
||||
}
|
||||
129
user/plugins/form/classes/Forms.php
Normal file
129
user/plugins/form/classes/Forms.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\Form;
|
||||
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Framework\Form\Interfaces\FormFactoryInterface;
|
||||
use Grav\Framework\Form\Interfaces\FormInterface;
|
||||
|
||||
class Forms
|
||||
{
|
||||
/** @var array|FormFactoryInterface[] */
|
||||
private $types;
|
||||
/** @var FormInterface|null */
|
||||
private $form;
|
||||
|
||||
/**
|
||||
* Forms constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->registerType('form', new FormFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param FormFactoryInterface $factory
|
||||
*/
|
||||
public function registerType(string $type, FormFactoryInterface $factory): void
|
||||
{
|
||||
$this->types[$type] = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function unregisterType($type): void
|
||||
{
|
||||
unset($this->types[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasType(string $type): bool
|
||||
{
|
||||
return isset($this->types[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTypes(): array
|
||||
{
|
||||
return array_keys($this->types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PageInterface $page
|
||||
* @param string|null $name
|
||||
* @param array|null $form
|
||||
* @return FormInterface|null
|
||||
*/
|
||||
public function createPageForm(PageInterface $page, string $name = null, array $form = null): ?FormInterface
|
||||
{
|
||||
if (null === $form) {
|
||||
[$name, $form] = $this->getPageParameters($page, $name);
|
||||
}
|
||||
|
||||
if (null === $form) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $form['type'] ?? 'form';
|
||||
$factory = $this->types[$type] ?? null;
|
||||
|
||||
if ($factory) {
|
||||
if (is_callable([$factory, 'createFormForPage'])) {
|
||||
return $factory->createFormForPage($page, $name, $form);
|
||||
}
|
||||
|
||||
if ($page instanceof Page) {
|
||||
// @phpstan-ignore-next-line
|
||||
return $factory->createPageForm($page, $name, $form);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FormInterface|null
|
||||
*/
|
||||
public function getActiveForm(): ?FormInterface
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormInterface $form
|
||||
* @return void
|
||||
*/
|
||||
public function setActiveForm(FormInterface $form): void
|
||||
{
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PageInterface $page
|
||||
* @param string|null $name
|
||||
* @return array
|
||||
*/
|
||||
protected function getPageParameters(PageInterface $page, ?string $name): array
|
||||
{
|
||||
$forms = $page->getForms();
|
||||
|
||||
if ($name) {
|
||||
// If form with given name was found, use that.
|
||||
$form = $forms[$name] ?? null;
|
||||
} else {
|
||||
// Otherwise pick up the first form.
|
||||
$form = reset($forms) ?: null;
|
||||
$name = (string)key($forms);
|
||||
}
|
||||
|
||||
return [$name, $form];
|
||||
}
|
||||
}
|
||||
163
user/plugins/form/classes/TwigExtension.php
Normal file
163
user/plugins/form/classes/TwigExtension.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Grav\Plugin\Form;
|
||||
|
||||
use Grav\Framework\Form\Interfaces\FormInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* Class GravExtension
|
||||
* @package Grav\Common\Twig\Extension
|
||||
*/
|
||||
class TwigExtension extends AbstractExtension
|
||||
{
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('value_and_label', [$this, 'valueAndLabel'])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all functions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('prepare_form_fields', [$this, 'prepareFormFields'], ['needs_context' => true]),
|
||||
new TwigFunction('prepare_form_field', [$this, 'prepareFormField'], ['needs_context' => true]),
|
||||
new TwigFunction('include_form_field', [$this, 'includeFormField']),
|
||||
];
|
||||
}
|
||||
|
||||
public function valueAndLabel($value): array
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($value as $key => $label) {
|
||||
$list[] = ['value' => $key, 'label' => $label];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters form fields for the current parent.
|
||||
*
|
||||
* @param array $context
|
||||
* @param array $fields Form fields
|
||||
* @param string|null $parent Parent field name if available
|
||||
* @return array
|
||||
*/
|
||||
public function prepareFormFields(array $context, $fields, $parent = null): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
if (is_iterable($fields)) {
|
||||
foreach ($fields as $name => $field) {
|
||||
$field = $this->prepareFormField($context, $field, $name, $parent);
|
||||
if ($field) {
|
||||
$list[$field['name']] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters field name by changing dot notation into array notation.
|
||||
*
|
||||
* @param array $context
|
||||
* @param array $field Form field
|
||||
* @param string|int|null $name Field name (defaults to field.name)
|
||||
* @param string|null $parent Parent field name if available
|
||||
* @param array|null $options List of options to override
|
||||
* @return array|null
|
||||
*/
|
||||
public function prepareFormField(array $context, $field, $name = null, $parent = null, array $options = []): ?array
|
||||
{
|
||||
// Make sure that the field is a valid form field type and is not being ignored.
|
||||
if (empty($field['type']) || ($field['validate']['ignore'] ?? false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If field has already been prepared, we do not need to do anything.
|
||||
if (!empty($field['prepared'])) {
|
||||
return $field;
|
||||
}
|
||||
|
||||
// Check if we have just a list of fields (no name given).
|
||||
$fieldName = (string)($field['name'] ?? $name);
|
||||
if (!is_string($name) || $name === '') {
|
||||
// Look at the field.name and if not set, fall back to the key.
|
||||
$name = $fieldName;
|
||||
}
|
||||
|
||||
// Make sure that the field has a name.
|
||||
if ($name === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prefix name with the parent name if needed.
|
||||
if (str_starts_with($name, '.')) {
|
||||
$plainName = (string)substr($name, 1);
|
||||
$field['plain_name'] = $plainName;
|
||||
$name = $parent ? $parent . $name : $plainName;
|
||||
} elseif (isset($options['key'])) {
|
||||
$name = str_replace('*', $options['key'], $name);
|
||||
}
|
||||
|
||||
unset($options['key']);
|
||||
|
||||
// Set fields as readonly if form is in readonly mode.
|
||||
/** @var FormInterface $form */
|
||||
$form = $context['form'] ?? null;
|
||||
if ($form && method_exists($form, 'isEnabled') && !$form->isEnabled()) {
|
||||
$options['disabled'] = true;
|
||||
}
|
||||
|
||||
// Loop through options
|
||||
foreach ($options as $key => $option) {
|
||||
$field[$key] = $option;
|
||||
}
|
||||
|
||||
// Always set field name.
|
||||
$field['name'] = $name;
|
||||
$field['prepared'] = true;
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string|string[]|null $layouts
|
||||
* @param string|null $default
|
||||
* @return string[]
|
||||
*/
|
||||
public function includeFormField(string $type, $layouts = null, string $default = null): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ((array)$layouts as $layout) {
|
||||
$list[] = "forms/fields/{$type}/{$layout}-{$type}.html.twig";
|
||||
}
|
||||
$list[] = "forms/fields/{$type}/{$type}.html.twig";
|
||||
|
||||
if ($default) {
|
||||
foreach ((array)$layouts as $layout) {
|
||||
$list[] = "forms/fields/{$default}/{$layout}-{$default}.html.twig";
|
||||
}
|
||||
$list[] = "forms/fields/{$default}/{$default}.html.twig";
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
40
user/plugins/form/composer.json
Normal file
40
user/plugins/form/composer.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "getgrav/grav-plugin-form",
|
||||
"type": "grav-plugin",
|
||||
"description": "Grav Plugin Form",
|
||||
"keywords": ["form"],
|
||||
"homepage": "https://github.com/getgrav/grav-plugin-form",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Team Grav",
|
||||
"email": "devs@getgrav.org",
|
||||
"homepage": "https://getgrav.org",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getgrav/grav-plugin-form/issues",
|
||||
"irc": "https://chat.getgrav.org",
|
||||
"forum": "https://discourse.getgrav.org",
|
||||
"docs": "https://github.com/getgrav/grav-plugin-form/blob/master/README.md"
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.3.6 || ^8.0",
|
||||
"ext-json": "*",
|
||||
"google/recaptcha": "^1.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\Plugin\\Form\\": "classes/"
|
||||
},
|
||||
"classmap": [
|
||||
"form.php"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.3.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
77
user/plugins/form/composer.lock
generated
Normal file
77
user/plugins/form/composer.lock
generated
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e0e2ccc2fca6a3cb5d548cf61bb80f66",
|
||||
"packages": [
|
||||
{
|
||||
"name": "google/recaptcha",
|
||||
"version": "1.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/google/recaptcha.git",
|
||||
"reference": "614f25a9038be4f3f2da7cbfd778dc5b357d2419"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/google/recaptcha/zipball/614f25a9038be4f3f2da7cbfd778dc5b357d2419",
|
||||
"reference": "614f25a9038be4f3f2da7cbfd778dc5b357d2419",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.2.20|^2.15",
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7.5.11"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ReCaptcha\\": "src/ReCaptcha"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.",
|
||||
"homepage": "https://www.google.com/recaptcha/",
|
||||
"keywords": [
|
||||
"Abuse",
|
||||
"captcha",
|
||||
"recaptcha",
|
||||
"spam"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://groups.google.com/forum/#!forum/recaptcha",
|
||||
"issues": "https://github.com/google/recaptcha/issues",
|
||||
"source": "https://github.com/google/recaptcha"
|
||||
},
|
||||
"time": "2020-03-31T17:50:54+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^7.3.6 || ^8.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.3.6"
|
||||
},
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
BIN
user/plugins/form/fonts/zxx-camo.ttf
Normal file
BIN
user/plugins/form/fonts/zxx-camo.ttf
Normal file
Binary file not shown.
BIN
user/plugins/form/fonts/zxx-noise.ttf
Normal file
BIN
user/plugins/form/fonts/zxx-noise.ttf
Normal file
Binary file not shown.
BIN
user/plugins/form/fonts/zxx-sans.ttf
Normal file
BIN
user/plugins/form/fonts/zxx-sans.ttf
Normal file
Binary file not shown.
BIN
user/plugins/form/fonts/zxx-xed.ttf
Normal file
BIN
user/plugins/form/fonts/zxx-xed.ttf
Normal file
Binary file not shown.
1289
user/plugins/form/form.php
Normal file
1289
user/plugins/form/form.php
Normal file
File diff suppressed because it is too large
Load Diff
36
user/plugins/form/form.yaml
Normal file
36
user/plugins/form/form.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
enabled: true
|
||||
built_in_css: true
|
||||
inline_css: true
|
||||
refresh_prevention: false
|
||||
client_side_validation: true
|
||||
inline_errors: false
|
||||
files:
|
||||
multiple: false # To allow multiple files, default is single
|
||||
limit: 10 # Number of allowed files per field (multiple required)
|
||||
destination: 'self@' # Where to upload the files (path and self@, page@, theme@)
|
||||
avoid_overwriting: false # Prevent files with the same name to be overridden. Date prefix will be added
|
||||
random_name: false # Generate a random 15 long string name for the uploaded files
|
||||
filesize: 0 # Maximum file size allowed (in MB)
|
||||
accept: # List of mime/types or file extensions allowed (ie, image/*,.zip,.mp4)
|
||||
- image/*
|
||||
recaptcha:
|
||||
version: 2-checkbox
|
||||
theme: light
|
||||
site_key:
|
||||
secret_key:
|
||||
basic_captcha:
|
||||
type: characters # options: [characters | math]
|
||||
chars:
|
||||
length: 6 # number of chars to output
|
||||
font: zxx-noise.ttf # options: [zxx-noise.ttf | zxx-camo.ttf | zxx-xed.ttf | zxx-sans.ttf]
|
||||
bg: '#cccccc' # 6-char hex color
|
||||
text: '#333333' # 6-char hex color
|
||||
size: 24 # font size in px
|
||||
start_x: 5 # start position in x direction in px
|
||||
start_y: 30 # start position in y direction in px
|
||||
box_width: 135 # box width in px
|
||||
box_height: 40 # box height in px
|
||||
math:
|
||||
min: 1 # smallest digit
|
||||
max: 12 # largest digit
|
||||
operators: ['+','-','*'] # operators that can be used in math
|
||||
84
user/plugins/form/gulpfile.js
Normal file
84
user/plugins/form/gulpfile.js
Normal file
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var immutable = require('immutable');
|
||||
var gulpWebpack = require('gulp-webpack');
|
||||
var webpack = require('webpack');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var exec = require('child_process').execSync;
|
||||
var sass = require('gulp-sass');
|
||||
var cleancss = require('gulp-clean-css');
|
||||
var csscomb = require('gulp-csscomb');
|
||||
var rename = require('gulp-rename');
|
||||
var autoprefixer = require('gulp-autoprefixer');
|
||||
var pwd = exec('pwd').toString();
|
||||
|
||||
// configure the paths
|
||||
var watch_dir = './scss/**/*.scss';
|
||||
var src_dir = './scss/*.scss';
|
||||
var dest_dir = './assets';
|
||||
|
||||
var paths = {
|
||||
source: src_dir
|
||||
};
|
||||
|
||||
var plugins = {};
|
||||
var base = immutable.fromJS(require('./webpack.conf.js'));
|
||||
var options = {
|
||||
prod: base.mergeDeep({
|
||||
devtool: 'source-map',
|
||||
optimization: {minimize: true},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': { NODE_ENV: '"production"' }
|
||||
}),
|
||||
new webpack.ProvidePlugin(plugins)
|
||||
],
|
||||
output: {
|
||||
filename: 'form.min.js'
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// var compileJS = function(watch) {
|
||||
// var prodOpts = options.prod.set('watch', watch);
|
||||
//
|
||||
// return gulp.src('app/main.js')
|
||||
// .pipe(gulpWebpack(prodOpts.toJS()))
|
||||
// .pipe(gulp.dest('assets/'));
|
||||
// };
|
||||
|
||||
var compileCSS = function() {
|
||||
return gulp.src(paths.source)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(sass({outputStyle: 'compact', precision: 10})
|
||||
.on('error', sass.logError)
|
||||
)
|
||||
.pipe(sourcemaps.write())
|
||||
.pipe(autoprefixer())
|
||||
.pipe(gulp.dest(dest_dir))
|
||||
.pipe(csscomb())
|
||||
.pipe(cleancss())
|
||||
.pipe(rename({
|
||||
suffix: '.min'
|
||||
}))
|
||||
.pipe(gulp.dest(dest_dir));
|
||||
};
|
||||
|
||||
// gulp.task('js', function() {
|
||||
// compileJS(false);
|
||||
// });
|
||||
|
||||
gulp.task('css', function() {
|
||||
compileCSS();
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch(watch_dir, ['css']);
|
||||
// compileJS(true);
|
||||
});
|
||||
|
||||
gulp.task('all', ['css']);
|
||||
gulp.task('default', ['all']);
|
||||
15
user/plugins/form/hebe.json
Normal file
15
user/plugins/form/hebe.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"project":"grav-plugin-form",
|
||||
"platforms":{
|
||||
"grav":{
|
||||
"nodes":{
|
||||
"plugin":[
|
||||
{
|
||||
"source":"/",
|
||||
"destination":"/user/plugins/form"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
913
user/plugins/form/languages.yaml
Normal file
913
user/plugins/form/languages.yaml
Normal file
@@ -0,0 +1,913 @@
|
||||
en:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Form not validated. One or more required fields are missing."
|
||||
NONCE_NOT_VALIDATED: "Oops, your form has timed out, please reload the page and submit the form again."
|
||||
FILES: "Files Upload"
|
||||
FORM_ALREADY_SUBMITTED: "This form has already been submitted."
|
||||
ALLOW_MULTIPLE: "Allow More than one file"
|
||||
ALLOW_MULTIPLE_HELP: "Allows to select more than one file for upload."
|
||||
DESTINATION: "Destination"
|
||||
DESTINATION_HELP: "The location where the files should be uploaded to"
|
||||
ACCEPT: "Allowed MIME Types"
|
||||
ACCEPT_HELP: "A list of MIME Types that are allowed for upload"
|
||||
ERROR_BASIC_CAPTCHA: "Captcha failed for this form, please try again"
|
||||
ERROR_VALIDATING_CAPTCHA: "reCAPTCHA bot protection has identified this form submission is problematic"
|
||||
DATA_SUMMARY: "Here is the summary of what you wrote to us:"
|
||||
NO_FORM_DATA: "No form data available"
|
||||
RECAPTCHA: "reCAPTCHA"
|
||||
RECAPTCHA_VERSION: "Version"
|
||||
RECAPTCHA_VERSION_V2_CHECKBOX: "v2 - Checkbox"
|
||||
RECAPTCHA_VERSION_V2_INVISIBLE: "v2 - Invisible"
|
||||
RECAPTCHA_VERSION_V3_LATEST: "v3 - Latest"
|
||||
RECAPTCHA_THEME: "Theme"
|
||||
RECAPTCHA_THEME_LIGHT: "Light Theme"
|
||||
RECAPTCHA_THEME_DARK: "Dark Theme"
|
||||
RECAPTCHA_SITE_KEY: "Site key"
|
||||
RECAPTCHA_SITE_KEY_HELP: "For more info visit https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Secret key"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "For more info visit https://developers.google.com/recaptcha"
|
||||
GENERAL: "General"
|
||||
USE_BUILT_IN_CSS: "Use built-in CSS"
|
||||
USE_INLINE_CSS: "Use inline CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Cannot use "%s" outside of pages.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Unable to upload file %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Unable to move file %s to "%s"'
|
||||
FILEUPLOAD_ERR_OK: 'There is no error, the file uploaded with success'
|
||||
FILEUPLOAD_ERR_INI_SIZE: 'The uploaded file exceeds the max upload size'
|
||||
FILEUPLOAD_ERR_FORM_SIZE: 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML'
|
||||
FILEUPLOAD_ERR_PARTIAL: 'The uploaded file was only partially uploaded'
|
||||
FILEUPLOAD_ERR_NO_FILE: 'No file was uploaded'
|
||||
FILEUPLOAD_ERR_NO_TMP_DIR: 'Missing a temporary folder'
|
||||
FILEUPLOAD_ERR_CANT_WRITE: 'Failed to write file to disk'
|
||||
FILEUPLOAD_ERR_EXTENSION: 'A PHP extension stopped the file upload'
|
||||
FILEUPLOAD_ERR_UNKNOWN: 'Unknown file upload error'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Cancel upload'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Are you sure you want to cancel this upload?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Drop your files here or <strong>click in this area</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Your browser does not support drag and drop file uploads.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Please use the fallback form below to upload your files like in the olden days.'
|
||||
DROPZONE_FILE_TOO_BIG: 'File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "You can't upload files of this type."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "You can not upload any more files."
|
||||
DROPZONE_REMOVE_FILE: "Remove file"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Are you sure you want to delete this file?'
|
||||
DROPZONE_RESPONSE_ERROR: "Server responded with {{statusCode}} code."
|
||||
YES: "Yes"
|
||||
NO: "No"
|
||||
REFRESH_PREVENTION: "Refresh prevention"
|
||||
REFRESH_PREVENTION_HELP: "Use the form's unique ID to ensure the same form is not reprocessed when refreshing the browser"
|
||||
LIMIT: "Limit"
|
||||
LIMIT_HELP: "Maximum number of allowed files per field (multiple required)"
|
||||
FILESIZE: "Max Filesize"
|
||||
FILESIZE_HELP: "Maximum file size allowed (in MB), 0 = Use system default"
|
||||
AVOID_OVERWRITING: "Avoid overwriting"
|
||||
AVOID_OVERWRITING_HELP: "Prevent files with the same name to be overridden. Date prefix will be added"
|
||||
RANDOM_NAME: "Random name"
|
||||
RANDOM_NAME_HELP: "Generate a random 15 long string name for the uploaded files"
|
||||
CLIENT_SIDE_VALIDATION: "Client-side validation"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "By default forms will use HTML5 client validation as first line of defense"
|
||||
INLINE_ERRORS: "Inline errors"
|
||||
INLINE_ERRORS_HELP: "Useful with 'client-side validation' off, shows inline contextual form errors"
|
||||
RESOLUTION_MIN: "The {{attr}} was less than the minimum of {{min}}px <br />"
|
||||
RESOLUTION_MAX: "The {{attr}} was more than the maximum of {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Destination not specified"
|
||||
INVALID_MIME_TYPE: "The MIME type %s for the file %s is not accepted."
|
||||
INVALID_FILE_EXTENSION: "The File Extension for the file %s is not accepted."
|
||||
BASIC_CAPTCHA: "Basic Captcha"
|
||||
BASIC_CAPTCHA_TYPE: "Captcha challenge type"
|
||||
BASIC_CAPTCHA_LENGTH: "Number of characters"
|
||||
BASIC_CAPTCHA_FONT: "TTF Font"
|
||||
BASIC_CAPTCHA_SIZE: "Font size"
|
||||
BASIC_CAPTCHA_BG_COLOR: "Background color"
|
||||
BASIC_CAPTCHA_TEXT_COLOR: "Text color"
|
||||
BASIC_CAPTCHA_START_X: "Text start x-position"
|
||||
BASIC_CAPTCHA_START_Y: "Text start y-position"
|
||||
BASIC_CAPTCHA_BOX_WIDTH: "Image width"
|
||||
BASIC_CAPTCHA_BOX_HEIGHT: "Image height"
|
||||
BASIC_CAPTCHA_MATH_MIN: "Minimum number"
|
||||
BASIC_CAPTCHA_MATH_MAX: "Maximum number"
|
||||
BASIC_CAPTCHA_MATH_OPERATORS: "Mathematical operators (randomized)"
|
||||
|
||||
eu:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formularioa ez da baliozkotu. Beharrezkoa den eremu bat edo gehiago falta dira."
|
||||
NONCE_NOT_VALIDATED: "Arazo bat gertatu da. Egiaztatu sartutako informazioa eta bidali berriro formularioa."
|
||||
FILES: "Fitxategi Kargatzea"
|
||||
FORM_ALREADY_SUBMITTED: "Formulario hau dagoeneko bidali da."
|
||||
ALLOW_MULTIPLE: "Onartu fitxategi bat baino gehiago"
|
||||
ALLOW_MULTIPLE_HELP: "Fitxategi bat baino gehiago kargatzeko aukera ematen du."
|
||||
DESTINATION: "Helmuga"
|
||||
DESTINATION_HELP: "Fitxategiak non kokatu behar diren"
|
||||
ACCEPT: "Baimendutako MIME motak"
|
||||
ACCEPT_HELP: "Kargatu ahal diren MIME moten zerrenda"
|
||||
ERROR_VALIDATING_CAPTCHA: "Errorea reCAPTCHA egiaztatzerakoan"
|
||||
DATA_SUMMARY: "Hona hemen idatzi diguzunaren laburpena:"
|
||||
NO_FORM_DATA: "Formularioaren datuak ez daude eskuragarri"
|
||||
RECAPTCHA: "reCAPTCHA"
|
||||
RECAPTCHA_VERSION: "Bertsioa"
|
||||
RECAPTCHA_VERSION_V2_CHECKBOX: "v2 - Checkbox"
|
||||
RECAPTCHA_VERSION_V2_INVISIBLE: "v2 - Invisible"
|
||||
RECAPTCHA_VERSION_V3_LATEST: "v3 - Latest"
|
||||
RECAPTCHA_THEME: "Itxura"
|
||||
RECAPTCHA_THEME_LIGHT: "Itxura Argia"
|
||||
RECAPTCHA_THEME_DARK: "Itxura Iluna"
|
||||
RECAPTCHA_SITE_KEY: "Gunearen gakoa"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Gehiago jakiteko bisitatu https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Gako Sekretua"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Gehiago jakiteko bisitatu https://developers.google.com/recaptcha"
|
||||
GENERAL: "Orokorra"
|
||||
USE_BUILT_IN_CSS: "Erabili CSS integratua"
|
||||
USE_INLINE_CSS: "Erabili CSS lerroan"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Ezin da "%s" erabili orrialdeetatik kanpo.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: '%s: %s fitxategia ezin da kargatu'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: '%s fitxategia ezin da mugitu "%s"-ra'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Utzi kargatzea'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Ziur al zaude kargatzea bertan behera utzi nahi duzula?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Utzi zure fitxategiak hemen edo <strong> klik egin eremu honetan </strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Zure nabigatzaileak ez du jasaten fitxategiak arrastratzea.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Mesedez erabili beheko formularioa zure fitxategiak kargatzeko.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Fitxategiak asko pisatzen du ({{filesize}}MiB). Gehienezko tamaina: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Ezin dituzu mota honetako fitxategiak kargatu."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Ezin dituzu fitxategi gehiago kargatu."
|
||||
DROPZONE_REMOVE_FILE: "Fitxategia ezabatu"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Zihur al zaude fitxategia ezabatu nahi duzula?'
|
||||
DROPZONE_RESPONSE_ERROR: "Zerbitzariak{{statusCode}} kodearekin erantzun du."
|
||||
YES: "Bai"
|
||||
NO: "Ez"
|
||||
REFRESH_PREVENTION: "Eguneratze prebentzioa"
|
||||
REFRESH_PREVENTION_HELP: "Erabili formularioaren ID bakarra, zihurtatzeko ez dela berriro prozesatuko nabigatzailea eguneratzerakoan."
|
||||
LIMIT: "Muga"
|
||||
LIMIT_HELP: "Baimendutako gehienezko fitxategi kopurua eremu bakoitzeko (beharrezkoak dira fitxategi anitz)"
|
||||
FILESIZE: "Gehienezko fitxategi tamaina"
|
||||
FILESIZE_HELP: "Onartutako gehienezko fitxategi tamaina (MB-tan), 0 = Sistemaren lehenetsia"
|
||||
AVOID_OVERWRITING: "Gainean idaztea ekidin"
|
||||
AVOID_OVERWRITING_HELP: "Izen bereko fitxategiak gainean idaztea ekidin. Data aurrizkia gehituko da"
|
||||
RANDOM_NAME: "Ausazko izena"
|
||||
RANDOM_NAME_HELP: "Sortu ausazko 15 karakteredun kate luze bat, izen bezala, kargatutako fitxategientzat"
|
||||
CLIENT_SIDE_VALIDATION: "Bezeroaren aldeko baliozkotzea"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Lehenetsita, formularioek HTML5 bezeroaren aldeko baliozkotzea erabiliko dute lehenengo defentsa moduan."
|
||||
INLINE_ERRORS: "Erroreak lerroan"
|
||||
INLINE_ERRORS_HELP: "Erabilgarria den 'bezeroaren aldeko baliozkotzea' desgaituta dago, testuinguruko formulario erroreak erakusten ditu"
|
||||
RESOLUTION_MIN: "{{Attr}} atributua {{min}}px gutxienekoa baino txikiagoa izan zen <br />"
|
||||
RESOLUTION_MAX: "{{Attr}} atributua {{max}}px gehienekoa baino handiagoa izan da <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Helmuga ez da zehaztu"
|
||||
INVALID_MIME_TYPE: "%s fitxategiaren %s MIME mota ez da onartzen."
|
||||
INVALID_FILE_EXTENSION: "%s fitxategiaren luzapena ez da onartzen."
|
||||
|
||||
de:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formularwerte nicht gültig. Für ein oder mehrere erforderliche Felder fehlen Werte."
|
||||
NONCE_NOT_VALIDATED: "Ups, es gibt da ein Problem. Eingabewerte bitte noch mal prüfen und das Formular erneut absenden."
|
||||
FILES: "Dateien hochladen"
|
||||
FORM_ALREADY_SUBMITTED: "Formular wurde bereits versendet."
|
||||
ALLOW_MULTIPLE: "Erlaube mehr als eine Datei"
|
||||
ALLOW_MULTIPLE_HELP: "Erlaubt es, mehr als eine Datei zum Hochladen auszuwählen."
|
||||
DESTINATION: "Ziel"
|
||||
DESTINATION_HELP: "Das Ziel, wohin die Dateien hochgeladen werden sollen."
|
||||
ACCEPT: "Erlaubte MIME-Typen"
|
||||
ACCEPT_HELP: "Eine Liste von MIME-Typen, die hochgeladen werden dürfen."
|
||||
ERROR_VALIDATING_CAPTCHA: "Die Überprüfung des Captcha ist fehlgeschlagen."
|
||||
DATA_SUMMARY: "Hier die Zusammenfassung dessen, was du geschrieben hast:"
|
||||
YES: "Ja"
|
||||
NO: "Nein"
|
||||
NO_FORM_DATA: "Keine Formulardaten vorhanden"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Seitenschlüssel"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Für mehr Informationen besuche https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Privater Schlüssel"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Für mehr Informationen besuche https://developers.google.com/recaptcha"
|
||||
GENERAL: "General"
|
||||
USE_BUILT_IN_CSS: "Benutze eingebautes CSS"
|
||||
USE_INLINE_CSS: "Benutze inline CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: '"%s" kann nicht ausserhalb der Seiten verwendet werden.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Upload-Fehler %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Fehler beim Verschieben von %s nach "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Upload abgebrochen'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Bist du sicher, dass du diesen Upload abbrechen möchtest??'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Ziehe deine Dateien hierhin: <strong>Klicke hier</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Dein Browser unterstützt kein "drag and drop".'
|
||||
DROPZONE_FILE_TOO_BIG: 'Datei zu gross ({{filesize}}MiB). Maximale Dateigrösse: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Du kannst keine Dateien dieses Typs hochladen."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Du kannst keine weiteren Dateien hochladen."
|
||||
DROPZONE_REMOVE_FILE: "Datei entfernen"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Möchtest du diese Datei wirklich löschen?'
|
||||
DROPZONE_RESPONSE_ERROR: "Server antwortet mit {{statusCode}}."
|
||||
LIMIT_HELP: "Maximale anzahl erlaubter Dateien (Mehrfachauswahl erforderlich) "
|
||||
FILESIZE: "Maximale Dateigrösse"
|
||||
FILESIZE_HELP: "Maximale Dateigrösse (in MB), 0 = Benutze Standart"
|
||||
AVOID_OVERWRITING: "Überschreiben verhindern"
|
||||
AVOID_OVERWRITING_HELP: "Verhindert das Überschreiben und fügt einen Dateipräfix hinzu."
|
||||
RANDOM_NAME: "Zufälliger Name"
|
||||
RANDOM_NAME_HELP: "Generiert einen 15 Zeichen langen Zufallsnamen."
|
||||
CLIENT_SIDE_VALIDATION: "Klientseitige Überprüfung"
|
||||
INLINE_ERRORS: "Inline-Fehler"
|
||||
DESTINATION_NOT_SPECIFIED: "Zielort nicht definiert"
|
||||
INVALID_MIME_TYPE: "Der MIME-Typ %s für die Datei %s ist nicht erlaubt."
|
||||
INVALID_FILE_EXTENSION: "Die Dateiendung %s ist nicht erlaubt."
|
||||
|
||||
es:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Falló la validación del formulario. Uno o más campos obligatorios no fueron cubiertos."
|
||||
NONCE_NOT_VALIDATED: "Oops, hay un problema, por favor revise la información e intente enviar el formulario otra vez."
|
||||
FILES: "Subida de Ficheros"
|
||||
FORM_ALREADY_SUBMITTED: "Este formulario ya ha sido enviado"
|
||||
ALLOW_MULTIPLE: "Permitir más de un fichero"
|
||||
ALLOW_MULTIPLE_HELP: "Permitir seleccionar más de un fichero para subir."
|
||||
DESTINATION: "Destino"
|
||||
DESTINATION_HELP: "El lugar de destino al que subir los ficheros"
|
||||
ACCEPT: "MIME Types permitidos"
|
||||
ACCEPT_HELP: "Una lista de MIME Types que se permiten subir."
|
||||
ERROR_VALIDATING_CAPTCHA: "Error al comprobar el Captcha"
|
||||
DATA_SUMMARY: "Este es el resumen de lo que nos has escrito:"
|
||||
NO_FORM_DATA: "No hay datos disponibles del formulario"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Site key"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Para más información visita https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Secret key"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Para más información visita https://developers.google.com/recaptcha"
|
||||
GENERAL: "General"
|
||||
USE_BUILT_IN_CSS: "Utilizar CSS incorporado"
|
||||
USE_INLINE_CSS: "Utilizar CSS en línea"
|
||||
FILEUPLOAD_PREVENT_SELF: 'No se puede utilizar "%s" fuera de las páginas'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'No se puede subir el fichero %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'No se puede mover el fichero %s to "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Cancelar subida'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: '¿Estas seguro que quieres cancelar esta subida?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Arrastra tus ficheros aquí o <strong>haz click en esta área</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Tu navegador no soporta el arrastrar y soltar ficheros de subida.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Por favor utiliza el formulario alternativo que esta debajo, para subir ficheros como antiguamente.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Fichero demasiado grande ({{filesize}}MiB). Máximo tamaño: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "No puedes subir ficheros de este tipo."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "No puedes subir mas ficheros."
|
||||
DROPZONE_REMOVE_FILE: "Borrar fichero"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: '¿Estas seguro de que quieres borrar este fichero?'
|
||||
DROPZONE_RESPONSE_ERROR: "El servidor respondió con el código de estado {{statusCode}}."
|
||||
YES: "Si"
|
||||
NO: "No"
|
||||
REFRESH_PREVENTION: "Prevenir refresco"
|
||||
REFRESH_PREVENTION_HELP: "Utiliza el ID único del formulario para garantizar que el este no se vuelve a procesar cuando se refresca el navegador"
|
||||
LIMIT: "Límite"
|
||||
LIMIT_HELP: "Máximo número de ficheros permitidos por campo (múltiples requeridos)"
|
||||
FILESIZE: "Tamaño máximo de fichero"
|
||||
FILESIZE_HELP: "Tamaño máximo de fichero permitido (en MB), 0 = Utiliza el de por defecto del sistema"
|
||||
AVOID_OVERWRITING: "Evitar sobreescribir"
|
||||
AVOID_OVERWRITING_HELP: "Evita que los ficheros con el mismo nombre sean sobreescritos. Se agregará el prefijo de fecha"
|
||||
RANDOM_NAME: "Nombre aleatorio"
|
||||
RANDOM_NAME_HELP: "Genera una cadela aleatoria de 15 caracteres como nombre de fichero a subir"
|
||||
CLIENT_SIDE_VALIDATION: "Validación del lado del cliente"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Por defecto los formularios usarán validación HTML5 del lado del cliente como primera línea de defensa"
|
||||
INLINE_ERRORS: "Errores en línea"
|
||||
INLINE_ERRORS_HELP: "Útil con 'validación del lado del cliente' desactivado, muestra errores en el formulario contextual en línea"
|
||||
RESOLUTION_MIN: "{{attr}} fué menor que el mínimo {{min}}px <br />"
|
||||
RESOLUTION_MAX: "{{attr}} fué mayor que el máximo {{max}}px <br />"
|
||||
|
||||
fr:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formulaire non validé. Un ou plusieurs champs obligatoires sont manquants."
|
||||
NONCE_NOT_VALIDATED: "Oups, un problème est survenu. Veuillez vérifier votre saisie et soumettre à nouveau le formulaire."
|
||||
FILES: "Fichiers chargés"
|
||||
FORM_ALREADY_SUBMITTED: "Le formulaire a déjà été envoyé."
|
||||
ALLOW_MULTIPLE: "Autoriser plus d'un fichier"
|
||||
ALLOW_MULTIPLE_HELP: "Permet la sélection de plusieurs fichiers pour chargement."
|
||||
DESTINATION: "Destination"
|
||||
DESTINATION_HELP: "L'emplacement où les fichiers doivent être chargés."
|
||||
ACCEPT: "Autoriser les Types MIME"
|
||||
ACCEPT_HELP: "Liste des Types MIME autorisés au chargement"
|
||||
ERROR_VALIDATING_CAPTCHA: "Erreur lors de la validation du Captcha"
|
||||
DATA_SUMMARY: "Voici le résumé de ce que vous nous avez écrit :"
|
||||
NO_FORM_DATA: "Aucune donnée de formulaire disponible"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Clé du site"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Pour plus d'informations veuillez vous rendre sur https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Clé secrète"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Pour plus d'informations veuillez vous rendre sur https://developers.google.com/recaptcha"
|
||||
GENERAL: "Général"
|
||||
USE_BUILT_IN_CSS: "Utiliser les CSS natifs"
|
||||
FILEUPLOAD_PREVENT_SELF: "Impossible d'utiliser '%s' en dehors des pages."
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: "Impossible de charger le fichier %s: %s"
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Impossible de déplacer le fichier %s vers "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: "Annuler le chargement"
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: "Êtes-vous certain de vouloir annuler ce téléchargement ?"
|
||||
DROPZONE_DEFAULT_MESSAGE: "Glissez vos fichiers ici ou <strong>cliquez dans cette zone</strong>"
|
||||
DROPZONE_FALLBACK_MESSAGE: "Votre navigateur ne prend pas en charge les téléchargements par glissé-déposé."
|
||||
DROPZONE_FALLBACK_TEXT: "Veuillez utiliser le formulaire de secours ci-dessous pour transférer vos fichiers."
|
||||
DROPZONE_FILE_TOO_BIG: "Le fichier est trop volumineux ({{filesize}}MiB). Taille maximale de fichier : {{maxFilesize}}MiB."
|
||||
DROPZONE_INVALID_FILE_TYPE: "Vous ne pouvez pas charger des fichiers de ce type."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Vous ne pouvez plus télécharger de fichiers."
|
||||
DROPZONE_REMOVE_FILE: "Supprimer le fichier"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: "Êtes-vous sûr de vouloir supprimer ce fichier ?"
|
||||
DROPZONE_RESPONSE_ERROR: "Le serveur a répondu avec le code {{statusCode}}."
|
||||
YES: "Oui"
|
||||
NO: "Non"
|
||||
REFRESH_PREVENTION: "Prévenir la redondance"
|
||||
REFRESH_PREVENTION_HELP: "Utilise l’identifiant unique du formulaire pour s’assurer qu’il n’est pas pris deux fois en compte si l’on recharge le navigateur."
|
||||
LIMIT: "Limite"
|
||||
LIMIT_HELP: "Nombre maximum de fichiers par champ (quand multiple)"
|
||||
FILESIZE: "Poids du fichier max."
|
||||
FILESIZE_HELP: "Poids maximum autorisé pour le fichier (en MB), 0 = utiliser la valeur du système par défaut"
|
||||
AVOID_OVERWRITING: "Éviter la réécriture"
|
||||
AVOID_OVERWRITING_HELP: "Évite qu’un fichier avec le même nom soit écrasé. Le nom sera préfixé de la date."
|
||||
RANDOM_NAME: "Nom aléatoire"
|
||||
RANDOM_NAME_HELP: "Génère une chaîne de caractère aléatoire de quinze caractères pour le nom du fichier uploadé."
|
||||
CLIENT_SIDE_VALIDATION: "Validation côté client"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Par défaut, les formulaires utiliseront la validation en HTML5 avant tout autre validation."
|
||||
INLINE_ERRORS: "Erreurs en ligne"
|
||||
INLINE_ERRORS_HELP: "Utile avec « Validation côté client » désactivé ; affiche des erreurs contextuelles en ligne."
|
||||
RESOLUTION_MIN: "{{attr}} est inférieur au minimum de {{min}}px <br />"
|
||||
RESOLUTION_MAX: "{{attr}} est supérieur au minimum de {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "La destination n’est pas indiquée."
|
||||
INVALID_MIME_TYPE: "Le type MIME %s pour le fichier %s n’est pas accepté."
|
||||
INVALID_FILE_EXTENSION: "L’extension pour le fichier %s n’est pas acceptée."
|
||||
|
||||
ru:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Форма не подтверждена. Отсутствует одно или несколько обязательных полей."
|
||||
NONCE_NOT_VALIDATED: "Упс, у вас возникла проблема, проверьте свои данные и отправьте форму еще раз."
|
||||
FILES: "Загрузка файлов"
|
||||
FORM_ALREADY_SUBMITTED: "Эта форма уже была отправлена."
|
||||
ALLOW_MULTIPLE: "Разрешить несколько файлов"
|
||||
ALLOW_MULTIPLE_HELP: "Позволяет выбрать более одного файла для загрузки."
|
||||
DESTINATION: "Место назначения"
|
||||
DESTINATION_HELP: "Место, куда файлы должны быть загружены в"
|
||||
ACCEPT: "Разрешенные MIME типы"
|
||||
ACCEPT_HELP: "Список MIME типов, разрешенных для загрузки"
|
||||
ERROR_VALIDATING_CAPTCHA: "Ошибка проверки Captcha"
|
||||
DATA_SUMMARY: "Вот краткое изложение того, что вы нам написали:"
|
||||
NO_FORM_DATA: "Данные формы отсутствуют"
|
||||
RECAPTCHA: "reCAPTCHA"
|
||||
RECAPTCHA_VERSION: "Версия"
|
||||
RECAPTCHA_VERSION_V2_CHECKBOX: "v2 - Флажок"
|
||||
RECAPTCHA_VERSION_V2_INVISIBLE: "v2 - Невидимый"
|
||||
RECAPTCHA_VERSION_V3_LATEST: "v3 - Последний"
|
||||
RECAPTCHA_THEME: "Тема"
|
||||
RECAPTCHA_THEME_LIGHT: "Светлая тема"
|
||||
RECAPTCHA_THEME_DARK: "Темная тема"
|
||||
RECAPTCHA_SITE_KEY: "Ключ сайта (site key)"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Для получения дополнительной информации посетите https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Секретный ключ (secret key)"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Для получения дополнительной информации посетите https://developers.google.com/recaptcha"
|
||||
GENERAL: "Общие"
|
||||
USE_BUILT_IN_CSS: "Использовать встроенный CSS"
|
||||
USE_INLINE_CSS: "Использовать inline CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Нельзя использовать "%s" за пределами страниц.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Не удалось загрузить файл %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Не удалось переместить файл %s в "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Отменить загрузку'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Вы действительно хотите отменить эту загрузку?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Перетащите свои файлы сюда или <strong>щелкните в этой области</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Ваш браузер не поддерживает загрузку файлов с перетаскиванием.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Пожалуйста, используйте приведенную ниже форму для загрузки ваших файлов, как в старые времена.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Файл слишком большой ({{filesize}}мб). Максимальный размер файла: {{maxFilesize}}мб.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Вы не можете загружать файлы этого типа."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Вы не можете загружать больше файлов."
|
||||
DROPZONE_REMOVE_FILE: "Удалить файл"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Вы действительно хотите удалить этот файл?'
|
||||
DROPZONE_RESPONSE_ERROR: "Сервер ответил кодом {{statusCode}}."
|
||||
YES: "Да"
|
||||
NO: "Нет"
|
||||
REFRESH_PREVENTION: "Предотвращение обновления"
|
||||
REFRESH_PREVENTION_HELP: "Используйте уникальный идентификатор формы, чтобы гарантировать, что одна и та же форма не будет отправляться заново при обновлении страницы браузера"
|
||||
LIMIT: "Лимит"
|
||||
LIMIT_HELP: "Максимальное количество разрешенных файлов в поле (требуется несколько)"
|
||||
FILESIZE: "Максимальный размер файла"
|
||||
FILESIZE_HELP: "Максимально допустимый размер файла (в МБ), 0 = Использовать системные установки по умолчанию"
|
||||
AVOID_OVERWRITING: "Избегать перезаписи"
|
||||
AVOID_OVERWRITING_HELP: "Не допускать перезаписи файлов с тем же именем. Будет добавлен префикс с датой"
|
||||
RANDOM_NAME: "Случайное имя"
|
||||
RANDOM_NAME_HELP: "Создавать случайное строковое имя из 15 символов для загруженных файлов"
|
||||
CLIENT_SIDE_VALIDATION: "Проверка на стороне клиента"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "По умолчанию формы будут использовать HTML5 проверку клиента как первую степень защиты"
|
||||
INLINE_ERRORS: "Встроенные ошибки"
|
||||
INLINE_ERRORS_HELP: "Полезно с отключенной «проверкой на стороне клиента», отображает встроенные контекстные ошибки формы"
|
||||
RESOLUTION_MIN: "{{attr}} меньше минимума {{min}}px <br />"
|
||||
RESOLUTION_MAX: "The {{attr}} больше, чем максимум {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Назначение не указано"
|
||||
INVALID_MIME_TYPE: "Тип MIME %s для файла %s не принимается."
|
||||
INVALID_FILE_EXTENSION: "Расширение файла %s не является допустимым для файла."
|
||||
|
||||
uk:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Форма не підтверджена. Відсутнє одне або кілька обов'язкових полів."
|
||||
NONCE_NOT_VALIDATED: "Упс, у вас виникла проблема, перевірте свої дані і відправте форму ще раз."
|
||||
FILES: "Завантаження файлів"
|
||||
FORM_ALREADY_SUBMITTED: "Ця форма вже була відправлена."
|
||||
ALLOW_MULTIPLE: "Дозволити декілька файлів"
|
||||
ALLOW_MULTIPLE_HELP: "Дозволяє вибрати більше одного файлу для завантаження."
|
||||
DESTINATION: "Місце призначення"
|
||||
DESTINATION_HELP: "Місце, куди файли повинні бути завантажені в"
|
||||
ACCEPT: "Дозволені MIME типи"
|
||||
ACCEPT_HELP: "Перелік MIME типів, дозволених для завантаження"
|
||||
ERROR_VALIDATING_CAPTCHA: "Помилка перевірки Captcha"
|
||||
DATA_SUMMARY: "Ось стислий виклад того, що ви нам написали:"
|
||||
NO_FORM_DATA: "Дані форми відсутні"
|
||||
RECAPTCHA: "reCAPTCHA"
|
||||
RECAPTCHA_VERSION: "Версія"
|
||||
RECAPTCHA_SITE_KEY: "Ключ сайту (site key)"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Для отримання додаткової інформації відвідайте https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Секретний ключ (secret key)"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Для отримання додаткової інформації відвідайте https://developers.google.com/recaptcha"
|
||||
GENERAL: "Загальні"
|
||||
USE_BUILT_IN_CSS: "Використовувати вбудований CSS"
|
||||
USE_INLINE_CSS: "Використовувати inline CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Не можна використовувати "%s" за межами сторінок.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Не вдалося завантажити файл %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Не вдалося перемістити файл %s до "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Скасувати завантаження'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Ви дійсно хочете скасувати це завантаження?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Перетягніть свої файли сюди чи <strong>клацніть в цій області</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Ваш браузер не підтримує завантаження файлів з перетягуванням.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Будь ласка, використовуйте цю форму для завантаження ваших файлів, як в старі часи.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Файл занадто великий ({{filesize}}мб). Максимальний розмір файлу: {{maxFilesize}}мб.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Ви не можете завантажувати файли цього типу."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Ви не можете завантажувати більше файлів."
|
||||
DROPZONE_REMOVE_FILE: "Видалити файл"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Ви дійсно хочете видалити цей файл?'
|
||||
DROPZONE_RESPONSE_ERROR: "Сервер відповів кодом {{statusCode}}."
|
||||
YES: "Так"
|
||||
NO: "Ні"
|
||||
REFRESH_PREVENTION: "Запобігання оновлення"
|
||||
REFRESH_PREVENTION_HELP: "Використовуйте унікальний ідентифікатор форми, щоб гарантувати, що одна і та ж форма не буде відправлятися заново при оновленні сторінки браузера"
|
||||
LIMIT: "Ліміт"
|
||||
LIMIT_HELP: "Максимальна кількість дозволених файлів в поле (потрібно декілька)"
|
||||
FILESIZE: "Максимальний розмір файлу"
|
||||
FILESIZE_HELP: "Максимально допустимий розмір файлу (в МБ), 0 = Використовувати системні налаштування за замовчуванням"
|
||||
AVOID_OVERWRITING: "Уникати перезапису"
|
||||
AVOID_OVERWRITING_HELP: "Не допускати перезапису файлів з тим же ім'ям. Буде додано префікс з датою"
|
||||
RANDOM_NAME: "Випадкове ім'я"
|
||||
RANDOM_NAME_HELP: "Створювати випадкове строкове ім'я з 15 символів для завантажених файлів"
|
||||
CLIENT_SIDE_VALIDATION: "Перевірка на стороні клієнта"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "За замовчуванням форми будуть використовувати HTML5 перевірку клієнта як першу ступінь захисту"
|
||||
INLINE_ERRORS: "Вбудовані помилки"
|
||||
INLINE_ERRORS_HELP: "Корисно з відключеною «перевіркою на стороні клієнта», відображає вбудовані контекстні помилки форми"
|
||||
RESOLUTION_MIN: "{{attr}} менше, ніж мінімум {{min}}px <br />"
|
||||
RESOLUTION_MAX: "The {{attr}} більше, ніж максимум {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Призначення не вказано"
|
||||
INVALID_MIME_TYPE: "Тип MIME %s для файла %s не приймається."
|
||||
INVALID_FILE_EXTENSION: "Розширення файлу %s не є допустимим для файлу."
|
||||
|
||||
hr:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formular nije validiran. Jedan ili više traženih polja nedostaju."
|
||||
NONCE_NOT_VALIDATED: "Ups, došlo je do problema, molimo provjerite svoj unos i pokušajte opet."
|
||||
FILES: "Upload Fajlova"
|
||||
ALLOW_MULTIPLE: "Dopusti više od jednog fajla"
|
||||
DESTINATION: "Destinacija"
|
||||
DESTINATION_HELP: "Lokacija gdje bi fajlovi trebali biti uploadani"
|
||||
ACCEPT: "Dopušteni MIME Tipovi"
|
||||
ACCEPT_HELP: "Lista dopuštenih MIME Tipova koji su dozvoljeni za upload"
|
||||
ERROR_VALIDATING_CAPTCHA: "Greška pri validiranju Captcha"
|
||||
|
||||
hu:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Érvénytelen az űrlap. Egy vagy több kötelező mező nincs kitöltve."
|
||||
NONCE_NOT_VALIDATED: "Upsz, van egy kis probléma, kérlek nézd át az űrlapot, majd küldd el újra."
|
||||
FILES: "Fájlok Feltöltése"
|
||||
FORM_ALREADY_SUBMITTED: "Ez az űrlap már el lett küldve."
|
||||
ALLOW_MULTIPLE: "Több fájl feltöltése"
|
||||
ALLOW_MULTIPLE_HELP: "Engedélyezi egyszerre több állomány feltöltését."
|
||||
DESTINATION: "Feltöltés Helye"
|
||||
DESTINATION_HELP: "Ide lesznek feltöltve az állományok"
|
||||
ACCEPT: "Engedélyezett MIME-típusok"
|
||||
ACCEPT_HELP: "A feltölthető állományok MIME-típusainak listája"
|
||||
ERROR_VALIDATING_CAPTCHA: "Hiba lépett fel a Captcha validálása során"
|
||||
DATA_SUMMARY: "Üzenet összesítő:"
|
||||
NO_FORM_DATA: "Űrlap adatok nem elérhetőek."
|
||||
RECAPTCHA: "reCAPTCHA"
|
||||
RECAPTCHA_VERSION: "Verzió"
|
||||
RECAPTCHA_VERSION_V2_CHECKBOX: "v2 - Kijelölőnégyzet"
|
||||
RECAPTCHA_VERSION_V2_INVISIBLE: "v2 - Láthatatlan"
|
||||
RECAPTCHA_VERSION_V3_LATEST: "v3 - Legutolsó"
|
||||
RECAPTCHA_THEME: "Téma"
|
||||
RECAPTCHA_THEME_LIGHT: "Világos téma"
|
||||
RECAPTCHA_THEME_DARK: "Sötét téma"
|
||||
RECAPTCHA_SITE_KEY: "Site kulcs"
|
||||
RECAPTCHA_SITE_KEY_HELP: "További információért látogass el ide a https://developers.google.com/recaptcha webhelyre."
|
||||
RECAPTCHA_SECRET_KEY: "Secret kulcs"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "További információért látogass el ide a https://developers.google.com/recaptcha webhelyre."
|
||||
GENERAL: "Általános"
|
||||
USE_BUILT_IN_CSS: "Alapértelmezett CSS használata"
|
||||
USE_INLINE_CSS: "Beágyazott CSS használata"
|
||||
FILEUPLOAD_PREVENT_SELF: '"%s" nem használható oldalakon kívül.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Sikertelen fájlfeltöltés %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: '%s áthelyezése sikertelen ide: "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Feltöltés megszakítása'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Biztos megszakítsuk a feltöltést?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Húzza a fájlokat ide vagy <strong>kattintson a területre</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'A böngésző nem támogatja a drag and drop fájlfeltöltéseket.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Kérjük használja a kompatibilitási űrlapot vagy a hagyományos fájfeltöltést.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Túl nagy állomány ({{filesize}}MiB). Maximum fájlméret: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Ilyen típusú fájlok feltöltése nem megengedett."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "További fájlok feltöltése nem megengedett, elérte a maximumot."
|
||||
DROPZONE_REMOVE_FILE: "Fájl eltávolítása"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Biztos az eltávolításban?'
|
||||
DROPZONE_RESPONSE_ERROR: "Szerver hiba, kód: {{statusCode}}."
|
||||
YES: "Igen"
|
||||
NO: "Nem"
|
||||
REFRESH_PREVENTION: "Újratöltési védelem"
|
||||
REFRESH_PREVENTION_HELP: "Az űrlap egyedi azonosítójának használata annak érdekében hogy az űrlap ne legyen újrafeldolgozva oldalfrissítés esetén."
|
||||
LIMIT: "Limit"
|
||||
LIMIT_HELP: "Az egy mezőben megadható fájlok maximális száma. (multiple required)"
|
||||
FILESIZE: "Maximum fájlméret"
|
||||
FILESIZE_HELP: "Maximum engedélyezett fájlméret (MB-ban), 0 = Rendszer alapértelmezés használata."
|
||||
AVOID_OVERWRITING: "Felülírás elkerülése"
|
||||
AVOID_OVERWRITING_HELP: "A már létező fájlok felülírásának elkerülése, dátum előtag hozzáadásával a fájlnévhez."
|
||||
RANDOM_NAME: "Véletlenszerű név"
|
||||
RANDOM_NAME_HELP: "Generáljon egy 15 karakter hosszú véletlenszerű nevet a feltöltött fájloknak."
|
||||
CLIENT_SIDE_VALIDATION: "Kliens oldali validáció"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Az űrlapok HTML5 kliens oldali validációt használnak alapértelmezésként, mint első védelmi vonal."
|
||||
INLINE_ERRORS: "Beágyazott hibaüzenetek"
|
||||
INLINE_ERRORS_HELP: "Beágyazott kontextuális űrlaphibák megjelenítése, hasznos ha a 'Kliens oldali validáció' ki van kapcsolva."
|
||||
RESOLUTION_MIN: "A(z) {{attr}} érték kisebb mint a minimum {{min}}px <br />"
|
||||
RESOLUTION_MAX: "A(z) {{attr}} érték nagyobb mint a maximum {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "A feltöltés helye nincs megadva."
|
||||
INVALID_MIME_TYPE: "MIME-típus %s nem engedélyezett. Állomány: %s."
|
||||
INVALID_FILE_EXTENSION: "A(z) %s állomány kiterjesztése nem engedélyezett."
|
||||
|
||||
it:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Il Form risulta invalido. Uno o più campi risultano omessi."
|
||||
NONCE_NOT_VALIDATED: "Oops è stato riscontrato un errore, si prega di ricontrollare i dati inseriti e provare di nuovo."
|
||||
FILES: "Invio dei Files"
|
||||
ALLOW_MULTIPLE: "Consenti più di un file"
|
||||
ALLOW_MULTIPLE_HELP: "Permette la selezione di più di un file per l'upload"
|
||||
DESTINATION: "Destinazione"
|
||||
DESTINATION_HELP: "La destinazione dove i files vengono uploadati"
|
||||
ACCEPT: "Tipi di MIME Concessi"
|
||||
ACCEPT_HELP: "Una lista di tipi di MIME che sono permessi per l'upload"
|
||||
ERROR_VALIDATING_CAPTCHA: "Errore durante la validazione del Captcha"
|
||||
DATA_SUMMARY: "Ecco il riassunto di ciò che ci hai scritto:"
|
||||
NO_FORM_DATA: "Nessuna informazione disponibile"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Site key"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Per maggiori informazioni visita https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Chiave segreta"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Per maggiori informazioni visita https://developers.google.com/recaptcha"
|
||||
GENERAL: "Generale"
|
||||
USE_BUILT_IN_CSS: "Usa CSS incorporato"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Non si può usare "%s" fuori dalle pagine.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Impossibile caricare il file %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Impossibile muovere il file %s to "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Trasferimento annullato'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Sei sicuro di voler cancellare questo trasferimento?'
|
||||
DROPZONE_DEFAULT_MESSAGE: "Trascina qui i tuoi file o <strong>clicca su quest'area</strong>"
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Il tuo browser non supporta il trascinamento dei file per il trasferimento.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Utilizza il modulo di riserva qui sotto per caricare i tuoi file come ai vecchi tempi.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Il file è troppo grande ({{filesize}}MiB). Dimensione massima consentita: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Non puoi caricare questo tipo di file"
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Non puoi caricare ulteriori file, hai raggiunto il limite consentito."
|
||||
DROPZONE_REMOVE_FILE: "Rimuovi il file"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Sei sicuro di voler eliminare questo file??'
|
||||
DROPZONE_RESPONSE_ERROR: "Il Server ha risposto con il codice {{statusCode}}."
|
||||
YES: "Si"
|
||||
NO: "No"
|
||||
|
||||
ro:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formularul nu a fost validat. Unul sau mai multe câmpuri sunt goale."
|
||||
NONCE_NOT_VALIDATED: "Oops a apărut o problemă, vă rugăm verificați datele introduse și trimiteți formularul din nou."
|
||||
FILES: "Încărcare fișiere"
|
||||
ALLOW_MULTIPLE: "Permiteți mai multe fișiere"
|
||||
ALLOW_MULTIPLE_HELP: "Vă permite să selectați mai multe fișiere pentru încărcare."
|
||||
DESTINATION: "Destinație"
|
||||
DESTINATION_HELP: "Locația unde vor fi încărcate fișierele."
|
||||
ACCEPT: "Permite tipuri MIME "
|
||||
ACCEPT_HELP: "O listă cu tipuri MIME care sunt permise la încărcare."
|
||||
ERROR_VALIDATING_CAPTCHA: "Eroare la validarea Captcha."
|
||||
DATA_SUMMARY: "Mai jos aveți un rezumat al mesajului pe care ni l-ați trimis:"
|
||||
NO_FORM_DATA: "Nu e disponibilă nici o dată pentru formular"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Cheia pentru Site"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Pentru mai multe detalii vă rugăm vizitați https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Cheia secretă pentru Site"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Pentru mai multe detalii vă rugăm vizitați https://developers.google.com/recaptcha"
|
||||
|
||||
cs:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formulář nebyl ověřen. Chybí jedno nebo více povinných polí."
|
||||
NONCE_NOT_VALIDATED: "Jejda, došlo k problému, zkontrolujte vstupní stránku a znovu odešlete formulář."
|
||||
FILES: "Nahrávání souborů"
|
||||
ALLOW_MULTIPLE: "Povolit více než jeden soubor"
|
||||
ALLOW_MULTIPLE_HELP: "Umožňuje vybrat více než jeden soubor pro nahrání."
|
||||
DESTINATION: "Cílové umístění"
|
||||
DESTINATION_HELP: "Místo, kam mají být soubory nahrány"
|
||||
ACCEPT: "Povolené MIME typy"
|
||||
ACCEPT_HELP: "Seznam MIME typů souborů povolených pro upload"
|
||||
ERROR_VALIDATING_CAPTCHA: "Nepodařilo se ověřit CAPTCHA (kontrola proti spamu)"
|
||||
DATA_SUMMARY: "Shrnutí toho, co jste nám napsali:"
|
||||
NO_FORM_DATA: "Formulář neobsahuje žádná data"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Site key"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Více informací https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Secret key"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Více informací https://developers.google.com/recaptcha"
|
||||
GENERAL: "Všeobecné"
|
||||
USE_BUILT_IN_CSS: "Použít built-in CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Nelze použít "% s" mimo stránky.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Nelze nahrát soubor %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Nelze přesunout soubor %s do "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Zrušit upload'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Opravdu chcete zrušit nahrávání souboru?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Přetáhněte sem soubory nebo <strong>klikněte v tomto prostoru</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Váš prohlížeč nepodporuje nahrávání souborů táhni a pusť.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Použijte níže uvedený formulář pro nahrání souborů, jako v minulých dnech.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Soubor je příliš velký ({{filesize}}MiB). Max. velikost souboru: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Nelze nahrát soubory tohoto typu."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Nelze nahrát další soubory."
|
||||
DROPZONE_REMOVE_FILE: "Odstranit soubor"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Opravdu chcete tento soubor smazat?'
|
||||
DROPZONE_RESPONSE_ERROR: "Server vrátil chybový kód: {{statusCode}}."
|
||||
YES: "Ano"
|
||||
NO: "Ne"
|
||||
pl:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formularz został odrzucony. Jedno lub więcej pól wymaga wypełnienia."
|
||||
NONCE_NOT_VALIDATED: "Ups, pojawił się problem. Proszę sprawdzić, czy wszystkie pola zostały wypełnione i wysłać formularz ponownie."
|
||||
FILES: "Plik przesłany."
|
||||
FORM_ALREADY_SUBMITTED: "Ten formularz został już wysłany."
|
||||
ALLOW_MULTIPLE: "Zezwól na więcej niż jeden plik."
|
||||
ALLOW_MULTIPLE_HELP: "Zezwól na przesłanie więcej niż jednego pliku."
|
||||
DESTINATION: "Cel"
|
||||
DESTINATION_HELP: "Lokalizacja przesłanego pliku."
|
||||
ACCEPT: "Dozwolone typy MIME"
|
||||
ACCEPT_HELP: "Lista dozwolonych do przesłania typów MIME"
|
||||
ERROR_VALIDATING_CAPTCHA: "Błąd walidacji Captcha"
|
||||
DATA_SUMMARY: "Oto podsumowanie informacji, które do nas napisałeś:"
|
||||
NO_FORM_DATA: "Brak dostępnych danych formularza"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Klucz strony"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Aby uzyskać więcej informacji, odwiedź https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Tajny klucz"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Aby uzyskać więcej informacji, odwiedź https://developers.google.com/recaptcha"
|
||||
GENERAL: "Ogólne"
|
||||
USE_BUILT_IN_CSS: "Użyj wbudowanych stylów CSS"
|
||||
USE_INLINE_CSS: "Użyj stylów inline CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Nie można użyć "%s" poza stronami.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Nie można przesłać pliku %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Nie można przenieść pliku %s do "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Anuluj przesyłanie'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Czy na pewno chcesz anulować to przesyłanie?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Upuść pliki tutaj lub <strong>kliknij w tym obszarze</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Twoja przeglądarka nie wspiera przesyłania plików metodą przeciągnij i upuść.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Proszę użyć zapasowego formularza do przesyłania plików znajdującego się poniżej.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Plik jest zbyt duży ({{filesize}}MiB). Maksymalna wielkość pliku: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Nie możesz przesłać pliku w tym formacie."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Nie możesz już przesłać więcej plików."
|
||||
DROPZONE_REMOVE_FILE: "Usuń plik"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Czy na pewno chcesz usunąć ten plik?'
|
||||
DROPZONE_RESPONSE_ERROR: "Serwer odpowiedział kodem {{statusCode}}."
|
||||
YES: "Tak"
|
||||
NO: "Nie"
|
||||
REFRESH_PREVENTION: "Zapobiegaj odświeżaniu"
|
||||
REFRESH_PREVENTION_HELP: "Użyj unikatowego identyfikatora ID, aby upewnić się, że przeglądarka nie przetworzy formularza ponownie."
|
||||
LIMIT: "Limit"
|
||||
LIMIT_HELP: "Maksymalna ilość dozwolonych plików na pole (wymagana liczba mnoga)"
|
||||
FILESIZE: "Maksymalny rozmiar pliku"
|
||||
FILESIZE_HELP: "Maksymalny dozwolony rozmiar pliku (w MB), 0 = użyj ustawień systemowych"
|
||||
AVOID_OVERWRITING: "Unikaj nadpisywania"
|
||||
AVOID_OVERWRITING_HELP: "Unikaj nadpisywania plików o tej samej nazwie. Zostanie dodany prefiks z datą"
|
||||
RANDOM_NAME: "Losowa nazwa"
|
||||
RANDOM_NAME_HELP: "Wygeneruj losowy ciąg 15 znaków jako nazwę dla przesyłanych plików"
|
||||
CLIENT_SIDE_VALIDATION: "Weryfikacja po stronie klienta"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Domyślnie formularze użyją weryfikacji HTML5 po stronie klienta jako pierwszej linii obrony"
|
||||
INLINE_ERRORS: "Błędy inline"
|
||||
INLINE_ERRORS_HELP: "Przydatne przy wyłączonej weryfikacji po stronie klienta, wyświetla kontekstowe błędy formularza"
|
||||
RESOLUTION_MIN: "{{attr}} była mniejsza od minimum wynoszącego {{min}}px <br />"
|
||||
RESOLUTION_MAX: "{{attr}} była większa od maksimum wynoszącego {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Cel nie został określony"
|
||||
INVALID_MIME_TYPE: "Typ MIME %s dla pliku %s nie jest dozwolony."
|
||||
INVALID_FILE_EXTENSION: "Rozszerzenie pliku %s nie jest dozwolone."
|
||||
nl:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formulier niet gevalideerd. Een of meerdere verplichte velden ontbreken."
|
||||
NONCE_NOT_VALIDATED: "Oeps er was een probleem, controleer je input en stuur het formulier opnieuw."
|
||||
FILES: "Bestanden geupload"
|
||||
FORM_ALREADY_SUBMITTED: "Dit formulier is al eerder verstuurd."
|
||||
ALLOW_MULTIPLE: "Meer dan een bestand toegestaan"
|
||||
ALLOW_MULTIPLE_HELP: "Staat toe dat meer dan een bestand geselecteerd kan worden voor uploaden."
|
||||
DESTINATION: "Bestemming"
|
||||
DESTINATION_HELP: "De lokatie waar de bestanden naartoe moeten worden geupload"
|
||||
ACCEPT: "Toegestane MIME types"
|
||||
ACCEPT_HELP: "Een lijst van MIME types welke zijn toegestaan om te uploaden"
|
||||
ERROR_VALIDATING_CAPTCHA: "Fout bij Captcha validatie"
|
||||
DATA_SUMMARY: "Hier is de samenvatting van wat u ons heeft gestuurd:"
|
||||
NO_FORM_DATA: "Geen formdata beschikbaar"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Site sleutel"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Voor meer informatie ga naar https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Geheime sleutel"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Voor meer informatie ga naar https://developers.google.com/recaptcha"
|
||||
GENERAL: "Algemeen"
|
||||
USE_BUILT_IN_CSS: "Gebruik ingebouwde CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Kan "%s" niet buiten paginas gebruiken.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Niet mogelijk om bestand %s: %s te uploaden'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Niet mogelijk om bestand %s naar "%s" te verplaatsen'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Upload annuleren'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Weet u zeker dat u deze upload wilt annuleren?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Sleep hier uw bestanden heen of <strong>klik in dit gebied</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Uw browser ondersteund geen drag en drop bestand uploads.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Gebruik alsublieft het fallback formulier hieronder om uw bestanden te uploaden zoals vroeger.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Bestand is te groot ({{filesize}}MiB). Maximale bestandsgrootte: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "U kunt geen bestanden uploaden van dit type."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "U kunt niet meer bestanden uploaden."
|
||||
DROPZONE_REMOVE_FILE: "Verwijder bestand"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Weet u zeker dat u het bestand wilt verwijderen?'
|
||||
DROPZONE_RESPONSE_ERROR: "Server reageert met {{statusCode}} code."
|
||||
YES: "Ja"
|
||||
NO: "Nee"
|
||||
REFRESH_PREVENTION: "Verversbescherming"
|
||||
REFRESH_PREVENTION_HELP: "Gebruik het unieke ID van her formulier om zeker te stellen dat hetzelfde formulier niet opnieuw wordt verwerkt als de browser ververst wordt"
|
||||
LIMIT: "Limiet"
|
||||
LIMIT_HELP: "Maximum aantal toegestande bestanden per veld (meerdere vereist)"
|
||||
FILESIZE: "Maximale bestandsgrootte"
|
||||
FILESIZE_HELP: "Maximale bestandsgrootte toegestaan (in MB), 0 = Gebruik systeem standaard"
|
||||
AVOID_OVERWRITING: "Vermijd overschrijven"
|
||||
AVOID_OVERWRITING_HELP: "Voorkom dat bestanden met dezelfde naam worden overschreven. Datum prefix wordt toegevoegd"
|
||||
RANDOM_NAME: "Willekeurige naam"
|
||||
RANDOM_NAME_HELP: "Genereer een willekeurige naam van 15 karakters voor de geuploade bestanden"
|
||||
pt:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formulário não validado. Falta preencher um ou mais campos obrigatórios."
|
||||
NONCE_NOT_VALIDATED: "Oops, houve um problema. Por favor verifique os dados informados e envie o formulário novamente."
|
||||
FILES: "Envio de Arquivos"
|
||||
FORM_ALREADY_SUBMITTED: "Este formulário já foi enviado."
|
||||
ALLOW_MULTIPLE: "Permitir mais de um arquivo"
|
||||
ALLOW_MULTIPLE_HELP: "Permite selecionar mais de um arquivo para enviar."
|
||||
DESTINATION: "Destino"
|
||||
DESTINATION_HELP: "O local para onde os arquivos devem ser enviados"
|
||||
ACCEPT: "Tipos MIME Permitidos"
|
||||
ACCEPT_HELP: "Uma lista de tipos MIME permitidos no upload"
|
||||
ERROR_VALIDATING_CAPTCHA: "Erro ao validar o captcha"
|
||||
DATA_SUMMARY: "Aqui está um resumo do que você escreveu:"
|
||||
NO_FORM_DATA: "Nenhum dado de formulário disponível"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Site key"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Para mais informações visite https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Secret key"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Para mais informações visite https://developers.google.com/recaptcha"
|
||||
GENERAL: "Geral"
|
||||
USE_BUILT_IN_CSS: "Usar CSS built-in"
|
||||
USE_INLINE_CSS: "Usar CSS inline"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Não é permitido usar "%s" fora de páginas.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Não foi possível enviar o arquivo %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Não foi possível mover o arquivo %s para "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Cancelar envio'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Tem certeza de que deseja cancelar este envio?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Arraste seus arquivos para cá ou <strong>clique nesta área</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Seu navegador não suporta arrastar e soltar arquivos.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Por favor use o formulário abaixo para enviar seus arquivos como antigamente.'
|
||||
DROPZONE_FILE_TOO_BIG: 'O arquivo é muito grande ({{filesize}}MiB). Tamanho máximo: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Você não pode enviar arquivos deste tipo."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Você não pode enviar mais arquivos."
|
||||
DROPZONE_REMOVE_FILE: "Remover arquivo"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Tem certeza de que deseja excluir este arquivo?'
|
||||
DROPZONE_RESPONSE_ERROR: "O servidor respondeu com código {{statusCode}}."
|
||||
YES: "Sim"
|
||||
NO: "Não"
|
||||
REFRESH_PREVENTION: "Evitar recarregamento"
|
||||
REFRESH_PREVENTION_HELP: "Usar o ID do formulário para garantir que ele não seja reprocessado se o usuário recarregar a página"
|
||||
LIMIT: "Limite"
|
||||
LIMIT_HELP: "Número máximo de arquivos permitidos por campo (deve ser múltiplo)"
|
||||
FILESIZE: "Tamanho máximo"
|
||||
FILESIZE_HELP: "Tamanho máximo de arquivo permitido (em MB), 0 = Use o default do sistema"
|
||||
AVOID_OVERWRITING: "Evitar sobrescrita"
|
||||
AVOID_OVERWRITING_HELP: "Prevenir que arquivos com o mesmo nome sejam sobrescritos. Serão adicionados prefixos com a data"
|
||||
RANDOM_NAME: "Nome aleatório"
|
||||
RANDOM_NAME_HELP: "Gerar um nome aleatório de 15 caracteres para os arquivos recebidos"
|
||||
CLIENT_SIDE_VALIDATION: "Validação no cliente"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Por padrão os formulários usarão a validação HTML5 no cliente como primeira linha de defesa"
|
||||
INLINE_ERRORS: "Erros inline"
|
||||
INLINE_ERRORS_HELP: "Útil se a validação no cliente estiver desabilitada. Mostra os erros próximo aos respectivos campos"
|
||||
RESOLUTION_MIN: "A {{attr}} é menor que o mínimo de {{min}}px <br />"
|
||||
RESOLUTION_MAX: "A {{attr}} é maior que o máximo de {{max}}px <br />"
|
||||
da:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formular ikke valideret. Et eller flere påkrævede felter mangler."
|
||||
NONCE_NOT_VALIDATED: "Ups der var et problem, tjek venligst dit input og send formularen igen."
|
||||
FILES: "Filupload"
|
||||
FORM_ALREADY_SUBMITTED: "Denne formular er allerede blevet sendt."
|
||||
ALLOW_MULTIPLE: "Tillad mere end én fil."
|
||||
ALLOW_MULTIPLE_HELP: "Tillader at der vælges mere end én fil til upload."
|
||||
DESTINATION: "Destination"
|
||||
DESTINATION_HELP: "Lokationen hvor filerne skal uploades til"
|
||||
ACCEPT: "Tilladte MIME Typer"
|
||||
ACCEPT_HELP: "En liste af MIME Typer der er tilladte for upload"
|
||||
ERROR_VALIDATING_CAPTCHA: "Fejl ved validering af Captcha"
|
||||
DATA_SUMMARY: "Her er resuméet af det, du skrev til os:"
|
||||
NO_FORM_DATA: "Ingen formular-data til rådighed"
|
||||
RECAPTCHA: "ReCaptcha"
|
||||
RECAPTCHA_SITE_KEY: "Site nøgle"
|
||||
RECAPTCHA_SITE_KEY_HELP: "For mere info besøg https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Hemmelig nøgle"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "For mere info besøg https://developers.google.com/recaptcha"
|
||||
GENERAL: "Generel"
|
||||
USE_BUILT_IN_CSS: "Brug indbygget CSS"
|
||||
USE_INLINE_CSS: "Brug inline CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Kan ikke bruge "%s" uden for sider.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Kan ikke uploade filen %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Kan ikke flytte fil %s til "%s"'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Annuller upload'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Er du sikker på, at du vil annullere denne upload?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Drop dine filer her eller <strong>klik i dette område</ strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Din browser understøtter ikke træk-og-slip filoploads.'
|
||||
DROPZONE_FALLBACK_TEXT: 'Brug venligst nedenstående formular til at uploade dine filer som i de gamle dage.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Filen er for stor ({{filesize}}MiB). Maks. filstørrelse: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Du kan ikke uploade filer af denne type."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Du kan ikke uploade flere filer."
|
||||
DROPZONE_REMOVE_FILE: "Fjern fil"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Er du sikker på, at du vil slette denne fil?'
|
||||
DROPZONE_RESPONSE_ERROR: "Server svarede med {{statusCode}} kode."
|
||||
YES: "Ja"
|
||||
NO: "Nej"
|
||||
REFRESH_PREVENTION: "Forebyg opdatering"
|
||||
REFRESH_PREVENTION_HELP: "Brug formularens unikke ID for at sikre, at samme form ikke bearbejdes igen, når du opdaterer browseren"
|
||||
LIMIT: "Begrænsning"
|
||||
LIMIT_HELP: "Maksimum antal tilladte filer pr. felt (flere påkrævede)"
|
||||
FILESIZE: "Maks. filstørrelse"
|
||||
FILESIZE_HELP: "Maksimal filstørrelse tilladt (i MB), 0 = Brug systemstandard"
|
||||
AVOID_OVERWRITING: "Undgå at overskrive"
|
||||
AVOID_OVERWRITING_HELP: "Forhindre at filer med samme navn overskrives. Dato præfiks vil blive tilføjet"
|
||||
RANDOM_NAME: "Tilfældigt navn"
|
||||
RANDOM_NAME_HELP: "Generer et tilfældigt 15 tegn langt strengnavn for de uploadede filer"
|
||||
CLIENT_SIDE_VALIDATION: "Validering på klientsiden"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Som standard bruger formularer HTML5-klient validering som første forsvarslinje"
|
||||
INLINE_ERRORS: "Inline fejl"
|
||||
INLINE_ERRORS_HELP: "Nyttig med 'validering på klientsiden' deaktiveret, viser inline kontekstuelle form fejl"
|
||||
RESOLUTION_MIN: "{{attr}} var mindre end minimum på {{min}}px <br />"
|
||||
RESOLUTION_MAX: "{{attr}} var højere end maksimum på {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Destination ikke angivet"
|
||||
INVALID_MIME_TYPE: "MIME typen %s for filen %s er ikke accepteret."
|
||||
INVALID_FILE_EXTENSION: "Filendelsen for filen %s er ikke accepteret."
|
||||
lt:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Forma nepatikrinta. Trūksta vieno ar daugiau privalomų laukų."
|
||||
NONCE_NOT_VALIDATED: "Oi, įvyko klaida. Prašome patikrinti įvestus duomenis ir pateikti formą iš naujo."
|
||||
FILES: "Bylų įkėlimas"
|
||||
FORM_ALREADY_SUBMITTED: "Ši forma jau buvo pateikta."
|
||||
ALLOW_MULTIPLE: "Leisti daugiau nei vieną bylą"
|
||||
ALLOW_MULTIPLE_HELP: "Leidžia pasirinkti daugiau nei vieną bylą įkėlimui."
|
||||
DESTINATION: "Tikslas"
|
||||
DESTINATION_HELP: "Vieta, kur bylos turėtų būti įkeltos"
|
||||
ACCEPT: "Leidžiami MIME tipai"
|
||||
ACCEPT_HELP: "MIME tipų sąrašas, kuriuos galima įkelti"
|
||||
ERROR_VALIDATING_CAPTCHA: "reCAPTCHA apsauga aptiko problemų pateikiant formą"
|
||||
DATA_SUMMARY: "Čia yra santrauka to, ką mums parašėte:"
|
||||
NO_FORM_DATA: "Nėra formos duomenų"
|
||||
RECAPTCHA: "reCAPTCHA"
|
||||
RECAPTCHA_VERSION: "Versija"
|
||||
RECAPTCHA_VERSION_V2_CHECKBOX: "v2 - žymimasis langelis"
|
||||
RECAPTCHA_VERSION_V2_INVISIBLE: "v2 - nematoma"
|
||||
RECAPTCHA_VERSION_V3_LATEST: "v3 - naujausia"
|
||||
RECAPTCHA_THEME: "Tema"
|
||||
RECAPTCHA_THEME_LIGHT: "Šviesi tema"
|
||||
RECAPTCHA_THEME_DARK: "Tamsi tema"
|
||||
RECAPTCHA_SITE_KEY: "Puslapio raktas"
|
||||
RECAPTCHA_SITE_KEY_HELP: "Daugiau informacijos - https://developers.google.com/recaptcha"
|
||||
RECAPTCHA_SECRET_KEY: "Slaptas raktas"
|
||||
RECAPTCHA_SECRET_KEY_HELP: "Daugiau informacijos - https://developers.google.com/recaptcha"
|
||||
GENERAL: "Bendra"
|
||||
USE_BUILT_IN_CSS: "Naudoti įdiegtą CSS"
|
||||
USE_INLINE_CSS: "Naudoti įterptą (inline) CSS"
|
||||
FILEUPLOAD_PREVENT_SELF: 'Negalima naudoti "%s" už puslapių ribų.'
|
||||
FILEUPLOAD_UNABLE_TO_UPLOAD: 'Nepavyko įkelti bylos %s: %s'
|
||||
FILEUPLOAD_UNABLE_TO_MOVE: 'Nepavyko perkelti bylos iš %s į "%s"'
|
||||
FILEUPLOAD_ERR_OK: 'Klaidos nėra, byla įkelta sėkmingai'
|
||||
FILEUPLOAD_ERR_INI_SIZE: 'Įkelta byla viršija maksimalų įkėlimo dydį'
|
||||
FILEUPLOAD_ERR_FORM_SIZE: 'Įkelta byla viršija HTML nurodytą MAX_FILE_SIZE direktyvą'
|
||||
FILEUPLOAD_ERR_PARTIAL: 'Įkeliama byla buvo įkelta tik dalinai'
|
||||
FILEUPLOAD_ERR_NO_FILE: 'Nebuvo įkelta jokia byla'
|
||||
FILEUPLOAD_ERR_NO_TMP_DIR: 'Nerastas laikinasis katalogas'
|
||||
FILEUPLOAD_ERR_CANT_WRITE: 'Nepavyko įrašyti bylos į diską'
|
||||
FILEUPLOAD_ERR_EXTENSION: 'PHP plėtinys sustabdė bylos įkėlimą'
|
||||
FILEUPLOAD_ERR_UNKNOWN: 'Nežinoma bylos įkėlimo klaida'
|
||||
DROPZONE_CANCEL_UPLOAD: 'Atšaukti įkėlimą'
|
||||
DROPZONE_CANCEL_UPLOAD_CONFIRMATION: 'Ar tikrai norite atšaukti šį įkėlimą?'
|
||||
DROPZONE_DEFAULT_MESSAGE: 'Tempkite bylas čia arba <strong>paspauskite šioje zonoje</strong>'
|
||||
DROPZONE_FALLBACK_MESSAGE: 'Jūsų naršyklė nepalaiko bylų įtempimo (drag and drop).'
|
||||
DROPZONE_FALLBACK_TEXT: 'Prašome bylų įkėlimui naudoti žemiau esantį atsarginį variantą, kaip senais laikais.'
|
||||
DROPZONE_FILE_TOO_BIG: 'Byla per didelė ({{filesize}}MiB). Maks. dydis: {{maxFilesize}}MiB.'
|
||||
DROPZONE_INVALID_FILE_TYPE: "Negalite įkelti šio tipo bylų."
|
||||
DROPZONE_MAX_FILES_EXCEEDED: "Nebegalite įkelti daugiau bylų."
|
||||
DROPZONE_REMOVE_FILE: "Šalinti bylą"
|
||||
DROPZONE_REMOVE_FILE_CONFIRMATION: 'Ar tikrai norite ištrinti šią bylą?'
|
||||
DROPZONE_RESPONSE_ERROR: "Serveris atsakė su {{statusCode}} kodu."
|
||||
YES: "Taip"
|
||||
NO: "Ne"
|
||||
REFRESH_PREVENTION: "Atnaujinimo apsauga"
|
||||
REFRESH_PREVENTION_HELP: "Naudoti formos unikalų ID, kad būtų sustabdytas formos pakartotinis apdorojimas atnaujinus puslapį"
|
||||
LIMIT: "Riba"
|
||||
LIMIT_HELP: "Maksimalus viename lauke leidžiamų bylų kiekis (privaloma leisti daugiau nei vieną)"
|
||||
FILESIZE: "Maks. bylos dydis"
|
||||
FILESIZE_HELP: "Maksimalus leidžiamas bylos dydis (MB), 0 = naudoti sistemos pagal nutylėjimą"
|
||||
AVOID_OVERWRITING: "Išvengti perrašymo"
|
||||
AVOID_OVERWRITING_HELP: "Neleisti perrašyti bylų tuo pačiu pavadinimu. Bus pridėtas datos priešdėlis"
|
||||
RANDOM_NAME: "Atsitiktinis pavadinimas"
|
||||
RANDOM_NAME_HELP: "Generuoti atsitiktinį 15-os simbolių ilgio pavadinimą įkeliamoms byloms"
|
||||
CLIENT_SIDE_VALIDATION: "Tikrinimas kliento pusėje"
|
||||
CLIENT_SIDE_VALIDATION_HELP: "Pagal nutylėjimą formos naudos HTML5 kliento tikrinimą kaip pirminį"
|
||||
INLINE_ERRORS: "Įterptos klaidos"
|
||||
INLINE_ERRORS_HELP: "Pravartu su išjungtu 'tikrinimu kliento pusėje' - parodo kontekstines formos klaidas"
|
||||
RESOLUTION_MIN: "{{attr}} buvo mažiau nei minimalus {{min}}px <br />"
|
||||
RESOLUTION_MAX: "{{attr}} buvo daugiau nei maksimalus {{max}}px <br />"
|
||||
DESTINATION_NOT_SPECIFIED: "Nenurodytas tikslas"
|
||||
INVALID_MIME_TYPE: "MIME tipas %s bylai %s nepriimtinas."
|
||||
INVALID_FILE_EXTENSION: "Bylos išplėtimas bylai %s nepriimtinas."
|
||||
45
user/plugins/form/package.json
Normal file
45
user/plugins/form/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "grav-plugin-form",
|
||||
"version": "1.0.0",
|
||||
"description": "Grav Plugin Form",
|
||||
"repository": "https://github.com/getgrav/grav-plugin-form",
|
||||
"main": "app/main.js",
|
||||
"scripts": {
|
||||
"watch": "NODE_ENV=development webpack --watch --progress --colors --mode development --config webpack.conf.js",
|
||||
"dev": "NODE_ENV=development webpack --progress --colors --config webpack.conf.js",
|
||||
"prod": "NODE_ENV=production-wip webpack --mode development --config webpack.conf.js"
|
||||
},
|
||||
"author": "Team Grav",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dropzone": "getgrav/dropzone#master",
|
||||
"exif-js": "^2.3.0",
|
||||
"sortablejs": "^1.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-loader": "^3.0.4",
|
||||
"exports-loader": "^0.7.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-autoprefixer": "^7.0.1",
|
||||
"gulp-clean-css": "^4.3.0",
|
||||
"gulp-csscomb": "^3.1.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-sass": "^4.0.2",
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"gulp-webpack": "^1.5.0",
|
||||
"immutable": "^4.0.0-rc.12",
|
||||
"imports-loader": "^0.8.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"style-loader": "^1.1.3",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11"
|
||||
}
|
||||
}
|
||||
2
user/plugins/form/scss.sh
Normal file
2
user/plugins/form/scss.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
sass --watch -s compressed scss:assets
|
||||
315
user/plugins/form/scss/form-styles.scss
Normal file
315
user/plugins/form/scss/form-styles.scss
Normal file
@@ -0,0 +1,315 @@
|
||||
$form-border-color: #ccc;
|
||||
$form-active-color: #000;
|
||||
|
||||
.form-group.has-errors {
|
||||
background: rgba(255,0,0,0.05);
|
||||
border: 1px solid rgba(255,0,0,0.2);
|
||||
border-radius: 3px;
|
||||
margin: 0 -5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.form-errors {
|
||||
color: #b52b27;
|
||||
}
|
||||
|
||||
.form-honeybear {
|
||||
visibility: hidden;
|
||||
position: absolute !important;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
}
|
||||
|
||||
.form-errors p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-input-file {
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dz-default.dz-message {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.dropzone {
|
||||
position: relative;
|
||||
min-height: 70px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: .85rem;
|
||||
border: 2px dashed #ccc;
|
||||
color: #aaa;
|
||||
padding: 0.5rem;
|
||||
|
||||
.dz-preview {
|
||||
margin: 0.5rem;
|
||||
|
||||
&:hover {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dz-error-message {
|
||||
min-width: 140px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.dz-image,
|
||||
&.dz-file-preview .dz-image {
|
||||
border-radius: 3px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// New JS powered tabs
|
||||
.form-tabs {
|
||||
|
||||
.tabs-nav {
|
||||
display: flex;
|
||||
padding-top: 1px;
|
||||
|
||||
margin-bottom: -1px;
|
||||
|
||||
a {
|
||||
flex: 1;
|
||||
transition: color 0.5s ease, background 0.5s ease;
|
||||
cursor: pointer;
|
||||
text-align:center;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid $form-border-color;
|
||||
border-radius: 5px 5px 0 0;
|
||||
|
||||
&.active {
|
||||
border: 1px solid $form-border-color;
|
||||
border-bottom: 1px solid transparent;
|
||||
margin: 0 -1px;
|
||||
|
||||
span {
|
||||
color: $form-active-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.subtle .tabs-nav {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.tabs-content {
|
||||
|
||||
.tab__content {
|
||||
display: none;
|
||||
padding-top: 2rem;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checkboxes
|
||||
.checkboxes {
|
||||
display: inline-block;
|
||||
|
||||
label {
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding: 0 0 0 20px;
|
||||
margin-right: 15px;
|
||||
|
||||
}
|
||||
label:before {
|
||||
content:"";
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 10px;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
|
||||
border: 1px solid #e6e6e6;
|
||||
|
||||
}
|
||||
input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
input[type=checkbox]:checked + label:before {
|
||||
content:"\2713";
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.toggleable label{
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Toggleable
|
||||
.form-field-toggleable {
|
||||
.checkboxes.toggleable {
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.checkboxes + label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
// Toggles
|
||||
.switch-toggle {
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
border-radius: 3px;
|
||||
line-height: 35px;
|
||||
border: 1px solid $form-border-color;
|
||||
|
||||
input[type=radio] {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
padding: 0 15px;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
color: inherit;
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
|
||||
input.highlight:checked + label {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
input:checked + label {
|
||||
color: #fff;
|
||||
background: #999;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* Signature Pad */
|
||||
.signature-pad {
|
||||
position: relative;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
font-size: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 700px;
|
||||
max-height: 460px;
|
||||
border: 1px solid #f0f0f0;
|
||||
background-color: #fff;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.signature-pad--body {
|
||||
position: relative;
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
border: 1px solid #f6f6f6;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.signature-pad--body canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.02) inset;
|
||||
}
|
||||
|
||||
.signature-pad--footer {
|
||||
color: #C3C3C3;
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.signature-pad--actions {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
[data-grav-field="array"] .form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
[data-grav-field="array"] .form-row > input,
|
||||
[data-grav-field="array"] .form-row > textarea
|
||||
{
|
||||
margin: 0 0.5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-data.basic-captcha {
|
||||
.form-input-wrapper {
|
||||
border: 1px solid $form-border-color;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
.form-input-prepend {
|
||||
display: flex;
|
||||
color: #333;
|
||||
background-color: #ccc;
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
margin: 0;
|
||||
}
|
||||
button > svg {
|
||||
margin: 0 8px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
input.form-input {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
2
user/plugins/form/templates/form-messages.html.twig
Normal file
2
user/plugins/form/templates/form-messages.html.twig
Normal file
@@ -0,0 +1,2 @@
|
||||
{% include 'partials/form-messages.html.twig' %}
|
||||
{% do http_response_code(form.responseCode) %}
|
||||
2
user/plugins/form/templates/form-messages.json.twig
Normal file
2
user/plugins/form/templates/form-messages.json.twig
Normal file
@@ -0,0 +1,2 @@
|
||||
{% include 'partials/form-messages.json.twig' %}
|
||||
{% do http_response_code(form.responseCode) %}
|
||||
8
user/plugins/form/templates/form.html.twig
Normal file
8
user/plugins/form/templates/form.html.twig
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'partials/base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ content|raw }}
|
||||
{% include "forms/form.html.twig" %}
|
||||
|
||||
{% endblock %}
|
||||
1
user/plugins/form/templates/form.json.twig
Normal file
1
user/plugins/form/templates/form.json.twig
Normal file
@@ -0,0 +1 @@
|
||||
{% extends 'forms/ajax.json.twig' %}
|
||||
20
user/plugins/form/templates/formdata.html.twig
Normal file
20
user/plugins/form/templates/formdata.html.twig
Normal file
@@ -0,0 +1,20 @@
|
||||
{% extends 'partials/base.html.twig' %}
|
||||
|
||||
{% if form is null %}
|
||||
{% set form = grav.session.getFlashObject('form') %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
{{ content|raw }}
|
||||
|
||||
{% if form %}
|
||||
{% include 'partials/form-messages.html.twig' %}
|
||||
|
||||
<p>{{ 'PLUGIN_FORM.DATA_SUMMARY'|t }}</p>
|
||||
|
||||
{% include "forms/data.html.twig" %}
|
||||
{% else %}
|
||||
<div class="notices warning yellow"><p>{{ 'PLUGIN_FORM.NO_FORM_DATA'|t }}</p></div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
5
user/plugins/form/templates/forms/ajax.json.twig
Normal file
5
user/plugins/form/templates/forms/ajax.json.twig
Normal file
@@ -0,0 +1,5 @@
|
||||
{% if form_json_response %}
|
||||
{{ form_json_response|json_encode|raw }}
|
||||
{% else %}
|
||||
{}
|
||||
{% endif %}
|
||||
1
user/plugins/form/templates/forms/data.html.twig
Normal file
1
user/plugins/form/templates/forms/data.html.twig
Normal file
@@ -0,0 +1 @@
|
||||
{% extends "forms/default/data.html.twig" %}
|
||||
1
user/plugins/form/templates/forms/data.txt.twig
Normal file
1
user/plugins/form/templates/forms/data.txt.twig
Normal file
@@ -0,0 +1 @@
|
||||
{% extends "forms/default/data.txt.twig" %}
|
||||
77
user/plugins/form/templates/forms/default/data.html.twig
Normal file
77
user/plugins/form/templates/forms/default/data.html.twig
Normal file
@@ -0,0 +1,77 @@
|
||||
{% macro render_field(form, fields, scope) %}
|
||||
{% import _self as self %}
|
||||
|
||||
{% for index, field in fields %}
|
||||
{%- set show_field = attribute(field, "input@") ?? field.store ?? true %}
|
||||
{% if field.fields %}
|
||||
{%- set new_scope = field.nest_id ? scope ~ field.name ~ '.' : scope -%}
|
||||
{{- self.render_field(form, field.fields, new_scope) }}
|
||||
{% else %}
|
||||
{% if show_field %}
|
||||
{%- set value = form.value(scope ~ (field.name ?? index)) -%}
|
||||
{% if value %}
|
||||
{% block field %}
|
||||
<div>
|
||||
{% block field_label %}
|
||||
<strong>{{ field.label|t|e }}</strong>:
|
||||
{% endblock %}
|
||||
|
||||
{% block field_value %}
|
||||
{% if field.type == 'checkboxes' %}
|
||||
<ul>
|
||||
{% set use_keys = field.use is defined and field.use == 'keys' %}
|
||||
{% for key,value in form.value(scope ~ field.name) %}
|
||||
{% set index = (use_keys ? key : value) %}
|
||||
<li>{{ field.options[index]|t|e }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elseif field.type == 'radio' %}
|
||||
{% set value = form.value(scope ~ field.name) %}
|
||||
{{ field.options[value]|t|e }}
|
||||
{% elseif field.type == 'checkbox' %}
|
||||
{{ (form.value(scope ~ field.name) == 1) ? "GRAV.YES"|t|e : "GRAV.NO"|t|e }}
|
||||
{% elseif field.type == 'select' %}
|
||||
{% set value = form.value(scope ~ field.name) %}
|
||||
{% if value is iterable %}
|
||||
<ul>
|
||||
{% set use_keys = field.use is defined and field.use == 'keys' %}
|
||||
{% for key, val in value %}
|
||||
{% set index = (use_keys ? key : val) %}
|
||||
<li>{{ field.options[index]|t|e }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ field.options[value]|t|e }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set value = form.value(scope ~ field.name) %}
|
||||
{% if value is iterable %}
|
||||
<ul>
|
||||
{% for val in value %}
|
||||
{% if val is iterable %}
|
||||
<ul>
|
||||
{% for v in val %}
|
||||
<li>{{ string(v)|e }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<li>{{ string(val)|e }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ string(value)|e|nl2br }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% import _self as macro %}
|
||||
|
||||
{{ macro.render_field(form, form.fields, '') }}
|
||||
21
user/plugins/form/templates/forms/default/data.txt.twig
Normal file
21
user/plugins/form/templates/forms/default/data.txt.twig
Normal file
@@ -0,0 +1,21 @@
|
||||
{%- macro render_field(form, fields, scope) %}
|
||||
{%- import _self as self %}
|
||||
{%- for index, field in fields %}
|
||||
{%- set show_field = attribute(field, "input@") ?? field.store ?? true %}
|
||||
{%- if field.fields %}
|
||||
{%- set new_scope = field.nest_id ? scope ~ field.name ~ '.' : scope -%}
|
||||
{{- self.render_field(form, field.fields, new_scope) }}
|
||||
{%- else %}
|
||||
{%- if show_field %}
|
||||
{%- set value = form.value(scope ~ (field.name ?? index)) -%}
|
||||
{%- if value -%}
|
||||
{{- field.label|t|e }}: {{ string(value is iterable ? value|json_encode : value) ~ "\n" }}
|
||||
{%- endif -%}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
{%- import _self as macro %}
|
||||
{%- autoescape false %}
|
||||
{{- macro.render_field(form, form.fields, '') ~ "\n" }}
|
||||
{%- endautoescape %}
|
||||
158
user/plugins/form/templates/forms/default/field.html.twig
Normal file
158
user/plugins/form/templates/forms/default/field.html.twig
Normal file
@@ -0,0 +1,158 @@
|
||||
{% if not field.validate.ignore %}
|
||||
|
||||
{% use 'forms/layouts/field-variables.html.twig' %}
|
||||
{% block field_override_variables_before %}{% endblock %}
|
||||
|
||||
{% set field_name = (scope ~ field.name)|fieldName %}
|
||||
{% set vertical = field.style == 'vertical' %}
|
||||
|
||||
{% if not blueprints or (blueprints.schema.type(field.type)['input@'] ?? true) is same as(true) %}
|
||||
{% set default = field.default %}
|
||||
{% set toggleable = field.toggleable ?? false %}
|
||||
{% if toggleable %}
|
||||
{% set originalValue = originalValue ?? value %}
|
||||
{% set toggleableChecked = originalValue is not null %}
|
||||
{% elseif field.overridable %}
|
||||
{% set toggleable = true %}
|
||||
{% set default = form.getDefaultValue(field.name) ?? default %}
|
||||
{% set toggleableChecked = value is not null and value != default %}
|
||||
{% endif %}
|
||||
|
||||
{% set cookie_name = 'forms-' ~ form.name ~ '-' ~ field.name %}
|
||||
{% set value = value ?? get_cookie(cookie_name) %}
|
||||
{% set has_value = value is not same as(null) %}
|
||||
{% if not has_value %}
|
||||
{% set value = default %}
|
||||
{% endif %}
|
||||
|
||||
{% if (field.yaml or field.validate.type == 'yaml') and value is iterable %}
|
||||
{% set value = value|yaml %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set toggleable = false %}
|
||||
{% endif %}
|
||||
|
||||
{# DEPRECATED: Needed by old form fields; remove when backwards compatibility breaks are allowed #}
|
||||
{% set isDisabledToggleable = toggleable and not toggleableChecked %}
|
||||
|
||||
{% if toggleable %}
|
||||
{% set form_field_toggleable %}
|
||||
{% include 'forms/default/toggleable.html.twig' with {checked: toggleableChecked} %}
|
||||
{% endset %}
|
||||
{% endif %}
|
||||
|
||||
{% set errors = attribute(form.messages, field.name) %}
|
||||
{% set required = client_side_validation and field.validate.required in ['on', 'true', 1] %}
|
||||
{% set autofocus = (inline_errors == false) and field.autofocus in ['on', 'true', 1] %}
|
||||
|
||||
{% if inline_errors and errors %}
|
||||
{% set autofocus = true %}
|
||||
{% endif %}
|
||||
|
||||
{% set embed_outer_field_classes %}
|
||||
{% block outer_field_classes %}{% endblock %}
|
||||
{% endset %}
|
||||
|
||||
{# Field Classes #}
|
||||
{%- if errors %}{% set form_field_outer_core = form_field_outer_core ~ ' has-errors' %}{% endif -%}
|
||||
{%- if toggleable %}{% set form_field_outer_core = form_field_outer_core ~ ' form-field-toggleable' %}{% endif -%}
|
||||
|
||||
{% set layout_form_field_outer_classes = field.outerclasses %}
|
||||
{% set layout_form_field_outer_classes = layout_form_field_outer_classes|trim ~ ' ' ~ form_field_outer_classes %}
|
||||
{% set layout_form_field_outer_classes = layout_form_field_outer_classes|trim ~ ' ' ~ embed_outer_field_classes %}
|
||||
|
||||
{# Show Label logic #}
|
||||
{% set show_label = field.label is not same as(false) and field.display_label is not same as(false )%}
|
||||
|
||||
{# Label Classes #}
|
||||
{% set layout_form_field_outer_label_classes = ((form_field_outer_label_classes ?: 'form-label') ~ ' ' ~ field.labelclasses)|trim %}
|
||||
{% set layout_form_field_label_classes = (form_field_label_classes ?: 'inline')|trim %}
|
||||
{% set form_field_label_trim = toggleable ? 'toggleable' %}
|
||||
|
||||
{# Field Outer Data classes #}
|
||||
{% set layout_form_field_outer_data_classes = ((form_field_outer_data_classes ?: ' form-data') ~ ' ' ~ field.dataclasses)|trim %}
|
||||
|
||||
{# Field Wrapper classes #}
|
||||
{% set layout_form_field_wrapper_classes = ((form_field_wrapper_classes ?: ' form-input-wrapper') ~ ' ' ~ field.wrapper_classes)|trim %}
|
||||
|
||||
{# Field input classes #}
|
||||
{% if field|of_type('array') %}
|
||||
{% if field.classes %}
|
||||
{% set field = field|merge({'classes': field.classes ~ ' ' ~ block('field_input_classes')|trim }) %}
|
||||
{% else %}
|
||||
{% set field = field|merge({'classes': block('field_input_classes') }) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% set layout_form_field_input_classes = (form_field_input_classes ~ ' ' ~ field.classes)|trim %}
|
||||
|
||||
{# Inline error classes #}
|
||||
{% set form_field_inline_error_classes = form_field_inline_error_classes ?: ' form-errors' %}
|
||||
|
||||
{# Field extra classes #}
|
||||
{% set form_field_extra_wrapper_classes = 'form-extra-wrapper ' ~ field.wrapper_classes %}
|
||||
|
||||
{# Field For #}
|
||||
{% set form_field_for = toggleable ? 'toggleable_' ~ field.name : field.id|e %}
|
||||
|
||||
{# Field Label #}
|
||||
{% set form_field_label = field.markdown ? field.label|markdown(false) : field.label %}
|
||||
{% set form_field_label = form_field_label|default(field.name|capitalize)|t %}
|
||||
|
||||
{# Field Help #}
|
||||
{% if field.help %}
|
||||
{% set form_field_help = field.markdown ? field.help|t|markdown(false)|e : field.help|t|e %}
|
||||
{% endif %}
|
||||
|
||||
{# Field Requied #}
|
||||
{% set form_field_required = field.validate.required in ['on', 'true', 1] ? true : false %}
|
||||
|
||||
{# Field Description #}
|
||||
{% set form_field_description = field.markdown ? field.description|t|markdown(false)|raw : field.description|t|raw %}
|
||||
|
||||
{% extends 'forms/layouts/field.html.twig' %}
|
||||
|
||||
{% block global_attributes %}
|
||||
data-grav-field="{{ field.type }}"
|
||||
data-grav-disabled="{{ toggleable and toggleableChecked }}"
|
||||
data-grav-default="{{ default|json_encode()|e('html_attr') }}"
|
||||
{% endblock %}
|
||||
|
||||
{% block input_attributes %}
|
||||
class="{{ layout_form_field_input_classes|trim }} {{ field.size }}"
|
||||
{% if field.id is defined %}id="{{ field.id|e }}" {% endif %}
|
||||
{% if field.style is defined %}style="{{ field.style|e }}" {% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if field.placeholder %}placeholder="{{ field.placeholder|t|e('html_attr') }}"{% endif %}
|
||||
{% if autofocus %}autofocus="autofocus"{% endif %}
|
||||
{% if field.novalidate in ['on', 'true', 1] %}novalidate="novalidate"{% endif %}
|
||||
{% if field.readonly in ['on', 'true', 1] %}readonly="readonly"{% endif %}
|
||||
{% if field.autocomplete is defined %}autocomplete="{{ field.autocomplete }}"{% endif %}
|
||||
{% if field.autocapitalize in ['off', 'characters', 'words', 'sentences'] %}autocapitalize="{{ field.autocapitalize }}"{% endif %}
|
||||
{% if field.inputmode in ['none', 'text', 'decimal', 'numeric', 'tel', 'search', 'email', 'url'] %}inputmode="{{ field.inputmode }}"{% endif %}
|
||||
{% if field.tabindex %}tabindex="{{ field.tabindex }}"{% endif %}
|
||||
{% if field.spellcheck in ['true', 'false'] %}spellcheck="{{ field.spellcheck }}"{% endif %}
|
||||
{% if required %}required="required"{% endif %}
|
||||
{% if field.validate.pattern %}pattern="{{ field.validate.pattern|e }}"{% endif %}
|
||||
{% if field.validate.message %}title="{{ field.validate.message|t|e }}"
|
||||
{% elseif field.title is defined %}title="{{ field.title|t|e }}" {% endif %}
|
||||
|
||||
{# Support key/value and .name/.value styles #}
|
||||
{% if field.attributes is defined %}
|
||||
{% for key,attribute in field.attributes %}
|
||||
{% if attribute|of_type('array') %}
|
||||
{{ attribute.name }}="{{ attribute.value|e('html_attr') }}"
|
||||
{% else %}
|
||||
{{ key }}="{{ attribute|e('html_attr') }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Support for Custom data attributes#}
|
||||
{% if field.datasets %}
|
||||
{% for key, attribute in field.datasets %}
|
||||
data-{{ key }}="{{ attribute|e('html_attr') }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% endif %}
|
||||
17
user/plugins/form/templates/forms/default/fields.html.twig
Normal file
17
user/plugins/form/templates/forms/default/fields.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{% set fields = prepare_form_fields(fields, name) %}
|
||||
{% set originalValue = null %}
|
||||
{% if fields|length %}
|
||||
{% block outer_markup_field_open %}{% endblock %}
|
||||
{% for field_name, field in fields %}
|
||||
{% set value = form ? form.value(field.name) : data.value(field.name) %}
|
||||
{% set field_templates = include_form_field(field.type, field_layout, fallback_field ?? 'text') %}
|
||||
{% block inner_markup_field_open %}{% endblock %}
|
||||
{% block field %}
|
||||
{% include field_templates %}
|
||||
{% endblock %}
|
||||
{% block inner_markup_field_close %}{% endblock %}
|
||||
{% endfor %}
|
||||
{% block outer_markup_field_close %}{% endblock %}
|
||||
{% else %}
|
||||
{% block empty_fields_markup %}{% endblock %}
|
||||
{% endif %}
|
||||
208
user/plugins/form/templates/forms/default/form.html.twig
Normal file
208
user/plugins/form/templates/forms/default/form.html.twig
Normal file
@@ -0,0 +1,208 @@
|
||||
{% set form = form ?? grav.session.getFlashObject('form') %}
|
||||
{% set layout = layout ?? form.layout ?? 'default' %}
|
||||
{% set field_layout = field_layout ?? layout %}
|
||||
|
||||
{# Keep here for Backwards Compatibility #}
|
||||
{% include 'partials/form-messages.html.twig' %}
|
||||
|
||||
{% set scope = scope ?: form.scope is defined ? form.scope : 'data.' %}
|
||||
{% set multipart = '' %}
|
||||
{% set blueprints = blueprints ?? form.blueprint() %}
|
||||
{% set method = form.method|upper|default('POST') %}
|
||||
{% set client_side_validation = form.client_side_validation is not null ? form.client_side_validation : config.plugins.form.client_side_validation|defined(true) %}
|
||||
{% set inline_errors = form.inline_errors is not null ? form.inline_errors : config.plugins.form.inline_errors(false) %}
|
||||
|
||||
{% set data = data ?? form.data %}
|
||||
{% set context = context ?? data %}
|
||||
|
||||
{% for field in form.fields %}
|
||||
{% if (method == 'POST' and field.type == 'file') %}
|
||||
{% set multipart = ' enctype="multipart/form-data"' %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set action = action ?? (form.action ?: page.route ~ uri.params) %}
|
||||
{% set action = (action starts with 'http') or (action starts with '#') ? action : base_url ~ action %}
|
||||
{% set action = action|trim('/', 'right') %}
|
||||
|
||||
{% if (action == base_url_relative) %}
|
||||
{% set action = base_url_relative ~ '/' %}
|
||||
{% endif %}
|
||||
|
||||
{% if form.keep_alive %}
|
||||
{% if grav.browser.browser == 'msie' and grav.browser.version < 12 %}
|
||||
{% do assets.addJs('plugin://form/assets/object.assign.polyfill.js') %}
|
||||
{% endif %}
|
||||
{% do assets.addJs('plugin://form/assets/form.vendor.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% do assets.addJs('plugin://form/assets/form.min.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% endif %}
|
||||
|
||||
{% do assets.addInlineJs("
|
||||
window.GravForm = window.GravForm || {};
|
||||
window.GravForm.config = {
|
||||
current_url: '" ~ grav.route.withoutParams() ~"',
|
||||
current_params: " ~ grav.route.params|json_encode ~ ",
|
||||
param_sep: '" ~ config.system.param_sep ~ "',
|
||||
base_url_relative: '" ~ base_url_relative ~ "',
|
||||
form_nonce: '" ~ form.getNonce() ~ "',
|
||||
session_timeout: " ~ config.system.session.timeout ~ "
|
||||
};
|
||||
window.GravForm.translations = Object.assign({}, window.GravForm.translations || {}, { PLUGIN_FORM: {} });
|
||||
", {'group': 'bottom', 'position': 'before', 'priority': 100}) %}
|
||||
|
||||
{# Backwards Compatibility for block overrides #}
|
||||
{% set override_form_classes %}
|
||||
{% block form_classes -%}
|
||||
{{ form_outer_classes }} {{ form.classes }}
|
||||
{%- endblock %}
|
||||
{% endset %}
|
||||
|
||||
{% set override_inner_markup_fields_start %}
|
||||
{% block inner_markup_fields_start %}{% endblock %}
|
||||
{% endset %}
|
||||
|
||||
{% set override_inner_markup_fields_end %}
|
||||
{% block inner_markup_fields_end %}{% endblock %}
|
||||
{% endset %}
|
||||
|
||||
{% set override_inner_markup_fields %}
|
||||
{% block inner_markup_fields %}
|
||||
{% for field_name, field in form.fields %}
|
||||
{% set field = prepare_form_field(field, field_name) %}
|
||||
{% if field %}
|
||||
{% set value = form ? form.value(field.name) : data.value(field.name) %}
|
||||
{% set field_templates = include_form_field(field.type, field_layout) %}
|
||||
|
||||
{% block inner_markup_field_open %}{% endblock %}
|
||||
{% block field %}
|
||||
{% include field_templates ignore missing %}
|
||||
{% endblock %}
|
||||
{% block inner_markup_field_close %}{% endblock %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% endset %}
|
||||
|
||||
{% set override_inner_markup_buttons_start %}
|
||||
{% block inner_markup_buttons_start %}
|
||||
<div class="{{ form_button_outer_classes ?: 'buttons'}}">
|
||||
{% endblock %}
|
||||
{% endset %}
|
||||
|
||||
{% set override_inner_markup_buttons_end %}
|
||||
{% block inner_markup_buttons_end %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endset %}
|
||||
|
||||
{# Embed for HTML layout #}
|
||||
{% embed 'forms/layouts/form.html.twig' %}
|
||||
|
||||
{% block embed_form_core %}
|
||||
name="{{ form.name }}"
|
||||
action="{{ action }}"
|
||||
method="{{ method }}"{{ multipart|raw }}
|
||||
{% if form.id %}id="{{ form.id }}"{% endif %}
|
||||
{% if form.novalidate %}novalidate{% endif %}
|
||||
{% if form.keep_alive %}data-grav-keepalive="true"{% endif %}
|
||||
{% if form.attributes is defined %}
|
||||
{% for key,attribute in form.attributes %}
|
||||
{% if attribute|of_type('array') %}
|
||||
{{ attribute.name }}="{{ attribute.value|e('html_attr') }}"
|
||||
{% else %}
|
||||
{{ key }}="{{ attribute|e('html_attr') }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block embed_form_classes -%}
|
||||
class="{{ parent() }} {{ override_form_classes|trim }}"
|
||||
{%- endblock %}
|
||||
|
||||
{% block embed_form_custom_attributes %}
|
||||
{% for k, v in blueprints.form.attributes %}
|
||||
{{ k }}="{{ v|e }}"
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block embed_fields %}
|
||||
{{ override_inner_markup_fields_start|raw }}
|
||||
{{ override_inner_markup_fields|raw }}
|
||||
|
||||
{% if form.isEnabled() ?? true %}
|
||||
{% include include_form_field('formname', field_layout, 'hidden') %}
|
||||
{% include include_form_field('formtask', field_layout, 'hidden') %}
|
||||
{% include include_form_field('uniqueid', field_layout, 'hidden') %}
|
||||
{% include include_form_field('nonce', field_layout, 'hidden') %}
|
||||
{% endif %}
|
||||
|
||||
{{ override_inner_markup_fields_end|raw }}
|
||||
{% endblock %}
|
||||
|
||||
{% block embed_buttons %}
|
||||
{{ override_inner_markup_buttons_start|raw }}
|
||||
|
||||
{% if form.isEnabled() ?? true %}
|
||||
{% for button in form.buttons %}
|
||||
{% if not button.access or authorize(button.access) %}
|
||||
{% if button.outerclasses is defined %}<div class="{{ button.outerclasses }}">{% endif %}
|
||||
|
||||
{% if button.url %}
|
||||
{% set button_url = button.url starts with 'http' ? button.url : base_url ~ button.url %}
|
||||
{% endif %}
|
||||
|
||||
{% embed 'forms/layouts/button.html.twig' %}
|
||||
{% block embed_button_core %}
|
||||
{% if button.id %}id="{{ button.id }}"{% endif %}
|
||||
{% if button.disabled %}disabled="disabled"{% endif %}
|
||||
{% if button.name %}
|
||||
name="{{ button.name }}"
|
||||
{% else %}
|
||||
{% if button.task %}name="task" value="{{ button.task }}"{% endif %}
|
||||
{% endif %}
|
||||
type="{{ button.type|default('submit') }}"
|
||||
{% if button.attributes is defined %}
|
||||
{% for key,attribute in button.attributes %}
|
||||
{% if attribute|of_type('array') %}
|
||||
{{ attribute.name }}="{{ attribute.value|e('html_attr') }}"
|
||||
{% else %}
|
||||
{{ key }}="{{ attribute|e('html_attr') }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block embed_button_classes %}
|
||||
{% block button_classes %}
|
||||
class="{{ form_button_classes ?: 'button' }} {{ button.classes }}"
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block embed_button_content -%}
|
||||
{%- set button_value = button.value|t|default('Submit') -%}
|
||||
{%- if button.html -%}
|
||||
{{- button_value|trim|raw -}}
|
||||
{%- else -%}
|
||||
{{- button_value|trim|e -}}
|
||||
{%- endif -%}
|
||||
{%- endblock %}
|
||||
|
||||
{% endembed %}
|
||||
|
||||
{% if button.outerclasses is defined %}</div>{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{{ override_inner_markup_buttons_end }}
|
||||
{% endblock %}
|
||||
|
||||
{% endembed %}
|
||||
|
||||
|
||||
{% if config.forms.dropzone.enabled %}
|
||||
<div id="dropzone-template" style="display:none;">
|
||||
{% include 'forms/dropzone/template.html.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,9 @@
|
||||
<span class="checkboxes toggleable" data-grav-field="toggleable" data-grav-field-name="{{ field_name }}">
|
||||
<input type="checkbox"
|
||||
id="toggleable_{{ field.name }}"
|
||||
name="toggleable_{{ field_name }}"
|
||||
value="1"
|
||||
{% if checked %}checked="checked"{% endif %}
|
||||
>
|
||||
<label for="toggleable_{{ field.name }}"></label>
|
||||
</span>
|
||||
@@ -0,0 +1,39 @@
|
||||
<div class="dz-preview dz-file-preview">
|
||||
<div class="dz-image"><img data-dz-thumbnail /></div>
|
||||
|
||||
<div class="dz-details">
|
||||
<div class="dz-size"><span data-dz-size></span></div>
|
||||
<div class="dz-filename"><span data-dz-name></span></div>
|
||||
</div>
|
||||
<div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
|
||||
<div class="dz-error-message"><span data-dz-errormessage></span></div>
|
||||
<a class="dz-remove" title="remove" href="javascript:undefined;" data-dz-remove>×</a>
|
||||
|
||||
<div class="dz-success-mark">
|
||||
|
||||
<svg width="54px" height="54px" viewBox="0 0 54 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.2.1 (9971) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Check</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<path d="M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z" id="Oval-2" stroke-opacity="0.198794158" stroke="#747474" fill-opacity="0.816519475" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="dz-error-mark">
|
||||
|
||||
<svg width="54px" height="54px" viewBox="0 0 54 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.2.1 (9971) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>error</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="Check-+-Oval-2" sketch:type="MSLayerGroup" stroke="#747474" stroke-opacity="0.198794158" fill="#FFFFFF" fill-opacity="0.816519475">
|
||||
<path d="M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z" id="Oval-2" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
9
user/plugins/form/templates/forms/field.html.twig
Normal file
9
user/plugins/form/templates/forms/field.html.twig
Normal file
@@ -0,0 +1,9 @@
|
||||
{#
|
||||
DO NOT MODIFY!
|
||||
|
||||
Default layout can be found in form plugin or your theme:
|
||||
|
||||
templates/forms/layouts/field/default-field.html.twig
|
||||
|
||||
#}
|
||||
{% extends "forms/default/field.html.twig" %}
|
||||
@@ -0,0 +1,99 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% macro renderer(key, text, field, scope) %}
|
||||
|
||||
{% if text is not iterable %}
|
||||
<div class="form-row{% if field.value_only %} array-field-value_only{% endif %}"
|
||||
data-grav-array-type="row">
|
||||
<span data-grav-array-action="sort" class="fa fa-bars"></span>
|
||||
{% if field.value_only != true %}
|
||||
{% if key == '0' and text == '' %}
|
||||
{% set key = '' %}
|
||||
{% endif %}
|
||||
|
||||
<input
|
||||
data-grav-array-type="key"
|
||||
type="text" value="{{ key }}"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
placeholder="{{ field.placeholder_key|e|t }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if field.value_type == 'textarea' %}
|
||||
<textarea
|
||||
data-grav-array-type="value"
|
||||
name="{{ ((scope ~ field.name)|fieldName) ~ '[' ~ key ~ ']' }}"
|
||||
placeholder="{{ field.placeholder_value|e|t }}"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}>{{ text }}</textarea>
|
||||
{% else %}
|
||||
<input
|
||||
data-grav-array-type="value"
|
||||
type="text"
|
||||
name="{{ ((scope ~ field.name)|fieldName) ~ '[' ~ key ~ ']' }}"
|
||||
placeholder="{{ field.placeholder_value|e|t }}"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
value={% if text == 'true' %}true{% elseif text == 'false' %}false{% else %}"{{ text|join(', ')|e }}"{% endif %} />
|
||||
{% endif %}
|
||||
|
||||
<span data-grav-array-action="rem" class="fa fa-minus"></span>
|
||||
<span data-grav-array-action="add" class="fa fa-plus"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% import _self as array_field %}
|
||||
|
||||
{% do assets.addJs('plugin://form/assets/form.vendor.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% do assets.addJs('plugin://form/assets/form.min.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
|
||||
{% block global_attributes %}
|
||||
data-grav-array-name="{{ (scope ~ field.name)|fieldName }}"
|
||||
data-grav-array-keyname="{{ field.placeholder_key|e|t }}"
|
||||
data-grav-array-valuename="{{ field.placeholder_value|e|t }}"
|
||||
data-grav-array-textarea="{{ field.value_type == 'textarea' }}"
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block input %}
|
||||
<div class="{{ field.size }} {{ field.classes }}" data-grav-array-type="container"{% if field.value_only %} data-grav-array-mode="value_only"{% endif %}{{ value|length <= 1 ? ' class="one-child"' : '' }}>
|
||||
{% if value|length %}
|
||||
{% for key, text in value -%}
|
||||
{% if text is not iterable %}
|
||||
{{ array_field.renderer(key, text, field, scope) }}
|
||||
{% else %}
|
||||
{# Backward compatibility for nested arrays (metas) which are not supported anymore #}
|
||||
{% for subkey, subtext in text -%}
|
||||
{{ array_field.renderer(key ~ '[' ~ subkey ~ ']', subtext, field, scope) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
{# Empty value, mock the entry field#}
|
||||
<div class="form-row" data-grav-array-type="row">
|
||||
<span data-grav-array-action="sort" class="fa fa-bars"></span>
|
||||
{% if field.value_only != true %}
|
||||
<input
|
||||
data-grav-array-type="key"
|
||||
type="text"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
placeholder="{{ field.placeholder_key|e|t }}" />
|
||||
{% endif %}
|
||||
{% if field.value_type == 'textarea' %}
|
||||
<textarea
|
||||
data-grav-array-type="value"
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
placeholder="{{ field.placeholder_value|e|t }}"></textarea>
|
||||
{% else %}
|
||||
<input
|
||||
data-grav-array-type="value"
|
||||
type="text"
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
placeholder="{{ field.placeholder_value|e|t }}" />
|
||||
{% endif %}
|
||||
<span data-grav-array-action="rem" class="fa fa-minus"></span>
|
||||
<span data-grav-array-action="add" class="fa fa-plus"></span>
|
||||
</div>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
{% set avatar = form.value('avatar') %}
|
||||
{% if avatar %}
|
||||
<label class="{{ field.classes }}"><img class="{{ field.img_classes }}" style="max-width:200px;" src="{{ base_url_simple ~ '/' ~ (avatar|first).path }}" /></label>
|
||||
{% else %}
|
||||
<label class="{{ field.classes }}"><img class="{{ field.img_classes }}" src="https://www.gravatar.com/avatar/{{ form.value('email')|md5 }}?s=200" /></label>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,28 @@
|
||||
{% set form_field_outer_data_classes = 'form-data basic-captcha' %}
|
||||
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block prepend %}
|
||||
<div class="form-input-addon form-input-prepend">
|
||||
<img id="basic-captcha-reload" src="{{ url('/forms-basic-captcha-image.jpg') }}" alt="human test" />
|
||||
<button id="reload-captcha"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="#292D32"><path d="M14.74 22.39c4.68-1.24 8-5.49 8-10.4 0-5.95-4.79-10.75-10.75-10.75 -3.11 0-5.78 1.11-7.99 2.95 -.77.64-1.43 1.32-1.98 2.01 -.34.41-.57.75-.69.95 -.22.35-.1.81.25 1.02 .35.21.81.09 1.02-.26 .08-.15.27-.43.56-.79 .49-.62 1.08-1.23 1.76-1.81C6.87 3.67 9.21 2.7 11.94 2.7c5.13 0 9.25 4.12 9.25 9.25 0 4.22-2.86 7.88-6.9 8.94 -.41.1-.64.51-.54.91 .1.4.51.63.91.53Zm-12-14.84V2.99c-.001-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75v4.56c0 .41.33.75.75.75 .41 0 .75-.34.75-.75Zm-.75.75H4h2.43c.41 0 .75-.34.75-.75 0-.42-.34-.75-.75-.75H4 1.99c-.42 0-.75.33-.75.75 0 .41.33.75.75.75Z"/><path d="M1.25 12c0 1.09.16 2.16.48 3.18 .12.39.54.61.93.49 .39-.13.61-.55.49-.94 -.28-.89-.42-1.81-.42-2.75 0-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75Zm1.93 6.15c.61.88 1.36 1.67 2.22 2.33 .32.25.79.19 1.05-.14 .25-.33.19-.8-.14-1.06 -.74-.58-1.38-1.25-1.92-2.02 -.24-.34-.71-.43-1.05-.19 -.34.23-.43.7-.19 1.04Zm5.02 3.91c1 .37 2.06.6 3.15.66 .41.02.76-.3.79-.71 .02-.42-.3-.77-.71-.8 -.94-.06-1.85-.25-2.72-.58 -.39-.15-.83.04-.97.43 -.15.38.04.82.43.96Z"/></g></svg></button>
|
||||
<script>
|
||||
function stripQueryString(url) {
|
||||
return url.split("?")[0].split("#")[0];
|
||||
}
|
||||
document.getElementById("reload-captcha").onclick = function(event) {
|
||||
event.preventDefault();
|
||||
const src = stripQueryString(document.getElementById("basic-captcha-reload").src);
|
||||
document.getElementById("basic-captcha-reload").src = src + `?v=${new Date().getTime()}`;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="text"
|
||||
{% if field.size %}size="{{ field.size }}"{% endif %}
|
||||
{% if field.minlength is defined or field.validate.min is defined %}minlength="{{ field.minlength | default(field.validate.min) }}"{% endif %}
|
||||
{% if field.maxlength is defined or field.validate.max is defined %}maxlength="{{ field.maxlength | default(field.validate.max) }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,100 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% set config = grav.config %}
|
||||
{% set site_key = field.recaptcha_site_key and field.recaptcha_site_key != 'ENTER_YOUR_CAPTCHA_SITE_KEY' ? field.recaptcha_site_key : config.plugins.form.recaptcha.site_key %}
|
||||
{% set action = (page.route|trim('/') ~ '-' ~ form.name)|underscorize %}
|
||||
{% set formName = form.name|underscorize %}
|
||||
{% set theme = config.plugins.form.recaptcha.theme ?? 'light' %}
|
||||
|
||||
{% block label %}{% endblock %}
|
||||
|
||||
{% block input %}
|
||||
{% if not site_key %}
|
||||
<script type="application/javascript">console && console.error('site_key was not defined for form "{{ form.name }}" (Grav Form Plugin)')</script>
|
||||
{% elseif config.plugins.form.recaptcha.version == 3 %}
|
||||
{% do assets.addJs('https://www.google.com/recaptcha/api.js?render='~site_key~'&theme=' ~ theme) %}
|
||||
{#<script src='https://www.google.com/recaptcha/api.js?render={{ site_key }}&theme={{ theme }}'></script>#}
|
||||
<script type="application/javascript">
|
||||
window.gRecaptchaInstances = window.gRecaptchaInstances || {};
|
||||
window.gRecaptchaInstances['{{ form.id }}'] = {
|
||||
element: document.querySelector('form#{{ form.id }}'),
|
||||
submit: function (event) {
|
||||
event.preventDefault();
|
||||
grecaptcha.ready(function () {
|
||||
grecaptcha.execute('{{ site_key }}', {action: '{{ action }}'}).then(function (token) {
|
||||
var tokenElement = document.createElement('input');
|
||||
tokenElement.setAttribute('type', 'hidden');
|
||||
tokenElement.setAttribute('name', 'data[token]');
|
||||
tokenElement.setAttribute('value', token);
|
||||
|
||||
var actionElement = document.createElement('input');
|
||||
actionElement.setAttribute('type', 'hidden');
|
||||
actionElement.setAttribute('name', 'data[action]');
|
||||
actionElement.setAttribute('value', '{{ action }}');
|
||||
|
||||
const form = window.gRecaptchaInstances['{{ form.id }}'].element;
|
||||
const submit = window.gRecaptchaInstances['{{ form.id }}'].submit;
|
||||
form.insertBefore(tokenElement, form.firstChild);
|
||||
form.insertBefore(actionElement, form.firstChild);
|
||||
form.removeEventListener('submit', submit);
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.gRecaptchaInstances['{{ form.id }}'].element.addEventListener('submit', window.gRecaptchaInstances['{{ form.id }}'].submit);
|
||||
</script>
|
||||
{% elseif config.plugins.form.recaptcha.version == '2-invisible' %}
|
||||
<script type="application/javascript">
|
||||
function captchaOnloadCallback_{{ formName }}() {
|
||||
var form = document.querySelector('form#{{ form.id }}');
|
||||
var submits = form.querySelectorAll('[type="submit"]') || [];
|
||||
submits.forEach(function(submit) {
|
||||
submit.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
var captchaElement = form.querySelector('#g-recaptcha-{{ formName }}');
|
||||
|
||||
if (captchaElement) {
|
||||
captchaElement.remove();
|
||||
}
|
||||
|
||||
captchaElement = document.createElement('div');
|
||||
captchaElement.setAttribute('id', 'g-recaptcha-{{ formName }}');
|
||||
form.appendChild(captchaElement);
|
||||
|
||||
var widgetReference = grecaptcha.render('g-recaptcha-{{ formName }}', {
|
||||
sitekey: '{{ site_key }}', size: 'invisible',
|
||||
callback: function(/* token */) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
|
||||
grecaptcha.execute(widgetReference);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=captchaOnloadCallback_{{ formName }}&hl={{ grav.language.language }}&theme={{ theme }}"
|
||||
async defer></script>
|
||||
{% else %}
|
||||
<script type="application/javascript">
|
||||
var captchaOnloadCallback_{{ formName }} = function captchaOnloadCallback_{{ formName }}() {
|
||||
grecaptcha.render('g-recaptcha-{{ formName }}', {
|
||||
'sitekey': "{{ site_key }}",
|
||||
'callback': captchaValidatedCallback_{{ formName }},
|
||||
'expired-callback': captchaExpiredCallback_{{ formName }}
|
||||
});
|
||||
};
|
||||
|
||||
var captchaValidatedCallback_{{ formName }} = function captchaValidatedCallback_{{ formName }}() {};
|
||||
var captchaExpiredCallback_{{ formName }} = function captchaExpiredCallback_{{ formName }}() {
|
||||
grecaptcha.reset();
|
||||
};
|
||||
</script>
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=captchaOnloadCallback_{{ formName }}&render=explicit&hl={{ grav.language.language }}&theme={{ theme }} "
|
||||
async defer></script>
|
||||
<div class="g-recaptcha" id="g-recaptcha-{{ formName }}" data-theme="{{ theme }}"></div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,46 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block label %}
|
||||
{% endblock %}
|
||||
|
||||
{% block input %}
|
||||
{% set id = field.id|default(field.name)|hyphenize %}
|
||||
<div class="{{ form_field_wrapper_classes ?: 'form-input-wrapper' }} {{ field.size }} {{ field.wrapper_classes }}">
|
||||
<input
|
||||
{# required attribute structures #}
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
value="{{ field.value ?? '1' }}"
|
||||
type="checkbox"
|
||||
{% if value == (field.value ?? '1') %} checked="checked" {% endif %}
|
||||
|
||||
{# input attribute structures #}
|
||||
{% block input_attributes %}
|
||||
id="{{ id|e }}"
|
||||
class="{{ form_field_checkbox_classes }} {{ field.classes }}"
|
||||
{% if field.style is defined %}style="{{ field.style|e }}" {% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if field.autofocus in ['on', 'true', 1] %}autofocus="autofocus"{% endif %}
|
||||
{% if field.novalidate in ['on', 'true', 1] %}novalidate="novalidate"{% endif %}
|
||||
{% if required %}required="required"{% endif %}
|
||||
{% if field.tabindex %}tabindex="{{ field.tabindex }}"{% endif %}
|
||||
{% if field.attributes is defined %}
|
||||
{% for key,attribute in field.attributes %}
|
||||
{% if attribute|of_type('array') %}
|
||||
{{ attribute.name }}="{{ attribute.value|e('html_attr') }}"
|
||||
{% else %}
|
||||
{{ key }}="{{ attribute|e('html_attr') }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
/>
|
||||
<label style="display:inline;" class="inline" for="{{ id|e }}">
|
||||
{% if field.markdown %}
|
||||
{{ field.label|t|markdown(false) }}
|
||||
{% else %}
|
||||
{{ field.label|t|e }}
|
||||
{% endif %}
|
||||
{{ field.validate.required in ['on', 'true', 1] ? '<span class="required">*</span>' }}
|
||||
</label>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,41 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block global_attributes %}
|
||||
{{ parent() }}
|
||||
data-grav-keys="{{ field.use == 'keys' ? 'true' : 'false' }}"
|
||||
data-grav-field-name="{{ (scope ~ field.name)|fieldName }}"
|
||||
{% endblock %}
|
||||
|
||||
{% block input %}
|
||||
{% set value = (value is null ? field.default : value) %}
|
||||
{% if field.use == 'keys' and field.default %}
|
||||
{% set value = field.default|merge(value) %}
|
||||
{% endif %}
|
||||
|
||||
{% for key, text in field.options %}
|
||||
|
||||
{% set id = field.id|default(field.name)|hyphenize ~ '-' ~ key %}
|
||||
{% set name = field.use == 'keys' ? key : id %}
|
||||
{% set val = field.use == 'keys' ? '1' : key %}
|
||||
{% set checked = (field.use == 'keys' ? value[key] : key in value) %}
|
||||
{% set help = (key in field.help_options|keys ? field.help_options[key] : false) %}
|
||||
|
||||
<div class="checkboxes {{ form_field_wrapper_classes }} {{ field.wrapper_classes }}">
|
||||
<input type="checkbox"
|
||||
id="{{ id|e }}"
|
||||
value="{{ val|e }}"
|
||||
name="{{ (scope ~ field.name)|fieldName ~ '[' ~ name ~ ']' }}"
|
||||
class="{{ form_field_checkbox_classes }} {{ field.classes }}"
|
||||
{% if checked %}checked="checked"{% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
>
|
||||
<label style="display: inline" for="{{ id|e }}">
|
||||
{% if help %}
|
||||
<span class="hint--bottom" data-hint="{{ help|t|e('html_attr') }}">{{ text|t|e }}</span>
|
||||
{% else %}
|
||||
{{ text|t|e }}
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,6 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="color"
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
{% embed 'forms/default/fields.html.twig' with {name: name, fields: field.fields} %}
|
||||
{% block outer_markup_field_open %}<div class="form-column {{ field.classes }}">{% endblock %}
|
||||
{% block outer_markup_field_close %}</div>{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
<div class="form-columns {{ field.classes }}">
|
||||
{% include 'forms/default/fields.html.twig' with {name: field.name|parent_field, fields: field.fields} %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
{% set value = evaluate(field.condition) %}
|
||||
{% set value = value == 'true' ? 1 : value %}
|
||||
{% set value = value == 'false' ? 0 : value %}
|
||||
|
||||
{% if value %}
|
||||
{% if field.classes %}
|
||||
<div class="{{ field.classes }}">
|
||||
{% endif %}
|
||||
|
||||
{% include 'forms/default/fields.html.twig' with {name: field.name, fields: field.fields} %}
|
||||
|
||||
{% if field.classes %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="date"
|
||||
{% if field.validate.min %}min="{{ field.validate.min }}"{% endif %}
|
||||
{% if field.validate.max %}max="{{ field.validate.max }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{{ value ? value|date('m/d/Y')|e }}
|
||||
@@ -0,0 +1,2 @@
|
||||
{# DEPRECATED. Switched to Text field until implemented properly #}
|
||||
{% extends "forms/fields/text/text.html.twig" %}
|
||||
@@ -0,0 +1 @@
|
||||
{{ value ? value|date('m/d/Y \\a\\t g:i A')|e }}
|
||||
@@ -0,0 +1,21 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% if field.file %}
|
||||
{% set content = read_file(field.file) %}
|
||||
{% else %}
|
||||
{% set content = field.content %}
|
||||
{% endif %}
|
||||
|
||||
{% block input %}
|
||||
<div class="form-display-wrapper {{ field.size }} {{ field.classes }}" {% if field.id is defined %}id="{{ field.id|e }}" {% endif %}>
|
||||
{% if field.markdown %}
|
||||
{{ content|t|markdown|raw }}
|
||||
{% else %}
|
||||
{% if field.evaluate %}
|
||||
{{ evaluate_twig(content)|raw }}
|
||||
{% else %}
|
||||
{{ content|t|raw }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="email"
|
||||
{% if field.multiple in ['on', 'true', 1] %}multiple="multiple"{% endif %}
|
||||
{% if field.size %}size="{{ field.size }}"{% endif %}
|
||||
{% if field.minlength is defined or field.validate.min is defined %}minlength="{{ field.minlength | default(field.validate.min) }}"{% endif %}
|
||||
{% if field.maxlength is defined or field.validate.max is defined %}maxlength="{{ field.maxlength | default(field.validate.max) }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
{% set scope = field.nest_id ? scope ~ field.name ~ '.' : scope %}
|
||||
|
||||
{% block field %}
|
||||
<fieldset {% if field.id is defined %}id="{{ field.id }}"{% endif %} {% if field.classes is defined %}class="{{ field.classes }}" {% endif %}>
|
||||
{% if field.legend %}
|
||||
<legend>{{ field.legend|t }}</legend>
|
||||
{% endif %}
|
||||
|
||||
{% include 'forms/default/fields.html.twig' with {name: field.name, fields: field.fields} %}
|
||||
</fieldset>
|
||||
{% endblock %}
|
||||
124
user/plugins/form/templates/forms/fields/file/file.html.twig
Normal file
124
user/plugins/form/templates/forms/fields/file/file.html.twig
Normal file
@@ -0,0 +1,124 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% macro bytesToSize(bytes) -%}
|
||||
{% spaceless %}
|
||||
{% set kilobyte = 1024 %}
|
||||
{% set megabyte = kilobyte * 1024 %}
|
||||
{% set gigabyte = megabyte * 1024 %}
|
||||
{% set terabyte = gigabyte * 1024 %}
|
||||
|
||||
{% if bytes < kilobyte %}
|
||||
{{ bytes ~ ' B' }}
|
||||
{% elseif bytes < megabyte %}
|
||||
{{ (bytes / kilobyte)|number_format(2, '.') ~ ' KB' }}
|
||||
{% elseif bytes < gigabyte %}
|
||||
{{ (bytes / megabyte)|number_format(2, '.') ~ ' MB' }}
|
||||
{% elseif bytes < terabyte %}
|
||||
{{ (bytes / gigabyte)|number_format(2, '.') ~ ' GB' }}
|
||||
{% else %}
|
||||
{{ (bytes / terabyte)|number_format(2, '.') ~ ' TB' }}
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro preview(path, value, global) %}
|
||||
{% if value %}
|
||||
{% set uri = global.grav.uri %}
|
||||
{% set files = global.files %}
|
||||
{% set config = global.grav.config %}
|
||||
{% set route = global.context.route() %}
|
||||
{% set type = global.context.content() is not null ? 'pages' : global.plugin ? 'plugins' : global.theme ? 'themes' : 'config' %}
|
||||
{% set blueprint_name = global.blueprints.getFilename %}
|
||||
{% if type == 'pages' %}
|
||||
{% set blueprint_name = type ~ '/' ~ blueprint_name %}
|
||||
{% endif %}
|
||||
{% set blueprint = blueprint_name|base64_encode %}
|
||||
{% set real_path = value.thumb ?? global.context.media[path].relativePath ?? global.form.getPagePathFromToken(path) %}
|
||||
{% set remove = global.form.getFileDeleteAjaxRoute(files.name, path)|string ?: uri.addNonce(
|
||||
global.base_url_relative ~
|
||||
'/media.json' ~
|
||||
'/task' ~ config.system.param_sep ~ 'removeFileFromBlueprint' ~
|
||||
'/proute' ~ config.system.param_sep ~ route|base64_encode ~
|
||||
'/blueprint' ~ config.system.param_sep ~ blueprint ~
|
||||
'/type' ~ config.system.param_sep ~ type ~
|
||||
'/field' ~ config.system.param_sep ~ files.name ~
|
||||
'/path' ~ config.system.param_sep ~ value.path|base64_encode, 'admin-form', 'admin-nonce') %}
|
||||
|
||||
{% set file = value|merge({remove: remove, path: value.thumb_url ?? (uri.rootUrl == '/' ? '/' : uri.rootUrl ~ '/' ~ real_path) }) %}
|
||||
<div class="hidden" data-file="{{ file|json_encode|e('html_attr') }}"></div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% import _self as macro %}
|
||||
|
||||
{% set defaults = config.plugins.form %}
|
||||
{% set files = defaults.files|merge(field|default([])) %}
|
||||
{% set limit = not field.multiple ? 1 : files.limit %}
|
||||
|
||||
{% do config.set('forms.dropzone.enabled', true) %}
|
||||
|
||||
{% block input %}
|
||||
{% set page_can_upload = exists or (type == 'page' and not exists and not (field.destination starts with '@self' or field.destination starts with 'self@')) %}
|
||||
{% set max_filesize = (field.filesize > form_max_filesize or field.filesize == 0) ? form_max_filesize : field.filesize %}
|
||||
|
||||
{% block prepend %}{% endblock %}
|
||||
{% set settings = {name: field.name, paramName: (scope ~ field.name)|fieldName ~ (files.multiple ? '[]' : ''), limit: limit, filesize: max_filesize, accept: files.accept, resolution: files.resolution, resizeWidth: files.resizeWidth, resizeHeight: files.resizeHeight, resizeQuality: files.resizeQuality } %}
|
||||
{% set dropzoneSettings = field.dropzone %}
|
||||
{% set file_url_add = form.getFileUploadAjaxRoute() %}
|
||||
{% set file_url_remove = form.getFileDeleteAjaxRoute(null, null) %}
|
||||
<div class="{{ form_field_wrapper_classes ?: 'form-input-wrapper' }} {{ field.classes }} dropzone files-upload form-input-file {{ field.size }}"
|
||||
data-grav-file-settings="{{ settings|json_encode|e('html_attr') }}"
|
||||
data-dropzone-options="{{ dropzoneSettings|json_encode|e('html_attr') }}"
|
||||
{% if file_url_add %}data-file-url-add="{{ file_url_add|e('html_attr') }}"{% endif %}
|
||||
{% if file_url_remove %}data-file-url-remove="{{ file_url_remove|e('html_attr') }}"{% endif %}>
|
||||
{% block file_extras %}{% endblock %}
|
||||
<input
|
||||
{# required attribute structures #}
|
||||
{% block input_attributes %}
|
||||
type="file"
|
||||
{% if files.multiple %}multiple="multiple"{% endif %}
|
||||
{% if files.accept %}accept="{{ files.accept|join(',') }}"{% endif %}
|
||||
{% if field.disabled %}disabled="disabled"{% endif %}
|
||||
{% if field.random_name %}random="true"{% endif %}
|
||||
{% if required %}required="required"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
/>
|
||||
|
||||
{% for path, file in value %}
|
||||
{{ macro.preview(path, file, _context) }}
|
||||
{% endfor %}
|
||||
{% include 'forms/fields/hidden/hidden.html.twig' with {field: {name: '_json.' ~ field.name}, value: (value ?? [])|json_encode } %}
|
||||
</div>
|
||||
|
||||
{% if grav.browser.browser == 'msie' and grav.browser.version < 12 %}
|
||||
{% do assets.addJs('plugin://form/assets/object.assign.polyfill.js') %}
|
||||
{% endif %}
|
||||
{% do assets.addJs('jquery', 101) %}
|
||||
{% do assets.addJs('plugin://form/assets/form.vendor.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% do assets.addJs('plugin://form/assets/form.min.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% do assets.addCss('plugin://form/assets/dropzone.min.css', { 'group': 'form'}) %}
|
||||
{{ assets.css('form')|raw }}
|
||||
{% do assets.addInlineJs("
|
||||
window.GravForm = window.GravForm || {};
|
||||
window.GravForm = Object.assign({}, window.GravForm, {
|
||||
translations: {
|
||||
PLUGIN_FORM: {
|
||||
'DROPZONE_CANCEL_UPLOAD': " ~ 'PLUGIN_FORM.DROPZONE_CANCEL_UPLOAD'|t|json_encode ~ ",
|
||||
'DROPZONE_CANCEL_UPLOAD_CONFIRMATION': " ~ 'PLUGIN_FORM.DROPZONE_CANCEL_UPLOAD_CONFIRMATION'|t|json_encode ~ ",
|
||||
'DROPZONE_DEFAULT_MESSAGE': " ~ 'PLUGIN_FORM.DROPZONE_DEFAULT_MESSAGE'|t|json_encode ~ ",
|
||||
'DROPZONE_FALLBACK_MESSAGE': " ~ 'PLUGIN_FORM.DROPZONE_FALLBACK_MESSAGE'|t|json_encode ~ ",
|
||||
'DROPZONE_FALLBACK_TEXT': " ~ 'PLUGIN_FORM.DROPZONE_FALLBACK_TEXT'|t|json_encode ~ ",
|
||||
'DROPZONE_FILE_TOO_BIG': " ~ 'PLUGIN_FORM.DROPZONE_FILE_TOO_BIG'|t|json_encode ~ ",
|
||||
'DROPZONE_INVALID_FILE_TYPE': " ~ 'PLUGIN_FORM.DROPZONE_INVALID_FILE_TYPE'|t|json_encode ~ ",
|
||||
'DROPZONE_MAX_FILES_EXCEEDED': " ~ 'PLUGIN_FORM.DROPZONE_MAX_FILES_EXCEEDED'|t|json_encode ~ ",
|
||||
'DROPZONE_REMOVE_FILE': " ~ 'PLUGIN_FORM.DROPZONE_REMOVE_FILE'|t|json_encode ~ ",
|
||||
'DROPZONE_REMOVE_FILE_CONFIRMATION': " ~ 'PLUGIN_FORM.DROPZONE_REMOVE_FILE_CONFIRMATION'|t|json_encode ~ ",
|
||||
'DROPZONE_RESPONSE_ERROR': " ~ 'PLUGIN_FORM.DROPZONE_RESPONSE_ERROR'|t|json_encode ~ ",
|
||||
'RESOLUTION_MIN': " ~ 'PLUGIN_FORM.RESOLUTION_MIN'|t|json_encode ~ ",
|
||||
'RESOLUTION_MAX': " ~ 'PLUGIN_FORM.RESOLUTION_MAX'|t|json_encode ~ "
|
||||
}
|
||||
}
|
||||
});
|
||||
", {'group': 'bottom', 'position': 'before'}) %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
<input type="hidden" name="__form-name__" value="{{ form.name }}" />
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
{% if form.task %}
|
||||
<input type="hidden" name="task" value="{{ form.task }}" />
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
|
||||
{# Used if the field is being used directly outside of form #}
|
||||
{% set value = value ?? field.value ?? (field.evaluate ? evaluate(field.default) : field.default) %}
|
||||
|
||||
{# Evaluate support for the form #}
|
||||
{% if not has_value and value and field.evaluate %}
|
||||
{% set value = evaluate(value) %}
|
||||
{% endif %}
|
||||
{% set input_value = value is iterable ? value|join(',') : value|string %}
|
||||
|
||||
<input data-grav-field="hidden" data-grav-disabled="false" {% if field.id is defined %}id="{{ field.id|e }}" {% endif %}type="hidden" class="input" name="{{ (scope ~ field.name)|fieldName }}" value="{{ input_value|e('html_attr') }}" />
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,13 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
{% set input_value = value is iterable ? value|join(',') : value|string %}
|
||||
<input aria-hidden="true"
|
||||
type="text"
|
||||
{% if config.plugins.form.inline_css == true %}
|
||||
style="visibility:hidden;position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);"
|
||||
{% endif %}
|
||||
class="form-honeybear"
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
value="{{ input_value|e }}" />
|
||||
{% endblock %}
|
||||
28
user/plugins/form/templates/forms/fields/key/key.html.twig
Normal file
28
user/plugins/form/templates/forms/fields/key/key.html.twig
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input %}
|
||||
<div class="form-input-wrapper {{ field.size }}">
|
||||
{% set input_value = value is iterable ? value|join(',') : value|string %}
|
||||
<input
|
||||
type="text"
|
||||
value="{{ input_value|e }}"
|
||||
data-key-observe="{{ (scope ~ field.name)|fieldName }}"
|
||||
{% block input_attributes %}
|
||||
{% if field.classes is defined %}class="{{ field.classes }}" {% endif %}
|
||||
{% if field.id is defined %}id="{{ field.id|e }}" {% endif %}
|
||||
{% if field.style is defined %}style="{{ field.style|e }}" {% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if field.placeholder %}placeholder="{{ field.placeholder }}"{% endif %}
|
||||
{% if field.autofocus in ['on', 'true', 1] %}autofocus="autofocus"{% endif %}
|
||||
{% if field.novalidate in ['on', 'true', 1] %}novalidate="novalidate"{% endif %}
|
||||
{% if field.readonly in ['on', 'true', 1] %}readonly="readonly"{% endif %}
|
||||
{% if field.autocomplete in ['on', 'off'] %}autocomplete="{{ field.autocomplete }}"{% endif %}
|
||||
{% if field.tabindex %}tabindex="{{ field.tabindex }}"{% endif %}
|
||||
{% if field.validate.required in ['on', 'true', 1] %}required="required"{% endif %}
|
||||
{% if field.validate.pattern %}pattern="{{ field.validate.pattern }}"{% endif %}
|
||||
{% if field.validate.message %}title="{{ field.validate.message|e|t }}"
|
||||
{% elseif field.title is defined %}title="{{ field.title|e|t }}" {% endif %}
|
||||
{% endblock %}
|
||||
/>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,6 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="month"
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{{ nonce_field(form.getNonceAction() ?? 'form', form.getNonceName() ?? 'form-nonce')|raw }}
|
||||
@@ -0,0 +1,9 @@
|
||||
{% extends "forms/fields/text/text.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="number"
|
||||
{% if field.validate.min is defined %}min="{{ field.validate.min }}"{% endif %}
|
||||
{% if field.validate.max is defined %}max="{{ field.validate.max }}"{% endif %}
|
||||
{% if field.validate.step is defined %}step="{{ field.validate.step }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% set value = null %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="password"
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,21 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input %}
|
||||
{% for key, text in field.options %}
|
||||
{% set id = field.id|default(field.name) ~ '-' ~ key %}
|
||||
|
||||
<div class="radio {{ form_field_wrapper_classes }} {{ field.wrapper_classes }}">
|
||||
<input type="radio"
|
||||
value="{{ key|e }}"
|
||||
id="{{ id|e }}"
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
class="{{ form_field_radio_classes }} {{ field.classes }}"
|
||||
{% if key == value %}checked="checked" {% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if required %}required="required"{% endif %}
|
||||
{% if field.tabindex %}tabindex="{{ field.tabindex }}"{% endif %}
|
||||
/>
|
||||
<label style="display: inline" class="inline" for="{{ id|e }}">{{ text|t|raw }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,9 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="range"
|
||||
{% if field.validate.min %}min="{{ field.validate.min }}"{% endif %}
|
||||
{% if field.validate.max %}max="{{ field.validate.max }}"{% endif %}
|
||||
{% if field.validate.step %}step="{{ field.validate.step }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,21 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
{% set title_level = grav['admin'] is defined ? 'h1' : field.title_level|default('h3') %}
|
||||
|
||||
{% block field %}
|
||||
{% if field.security is empty or authorize(array(field.security)) %}
|
||||
<div class="{{ field.classes }}">
|
||||
{% if field.title or field.underline %}
|
||||
<{{ title_level }} class="{% if not field.underline %}no_underline{% endif %}">{{ field.title|t }}</{{ title_level }}>
|
||||
{% endif %}
|
||||
|
||||
{% if field.text %}
|
||||
<p>{{ field.text|t|raw }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% embed 'forms/default/fields.html.twig' with {name: field.name, fields: field.fields} %}
|
||||
{% block outer_markup_field_open %}<div class="form-section">{% endblock %}
|
||||
{% block outer_markup_field_close %}</div>{% endblock %}
|
||||
{% endembed %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,78 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block global_attributes %}
|
||||
data-grav-selectize="{{ (field.selectize is defined ? field.selectize : {})|json_encode()|e('html_attr') }}"
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block input %}
|
||||
<div class="{{ form_field_wrapper_classes ?: 'form-select-wrapper' }} {{ field.size }} {{ field.wrapper_classes }}">
|
||||
<select name="{{ (scope ~ field.name)|fieldName ~ (field.multiple ? '[]' : '') }}"
|
||||
class="{{ form_field_select_classes }} {{ field.classes }} {{ field.size }}"
|
||||
{% if field.id is defined %}id="{{ field.id|e }}" {% endif %}
|
||||
{% if field.style is defined %}style="{{ field.style|e }}" {% endif %}
|
||||
{% if field.disabled %}disabled="disabled"{% endif %}
|
||||
{% if field.autofocus in ['on', 'true', 1] %}autofocus="autofocus"{% endif %}
|
||||
{% if field.novalidate in ['on', 'true', 1] %}novalidate="novalidate"{% endif %}
|
||||
{% if required %}required="required"{% endif %}
|
||||
{% if field.multiple in ['on', 'true', 1] %}multiple="multiple"{% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if field.tabindex %}tabindex="{{ field.tabindex }}"{% endif %}
|
||||
{% if field.form %}form="{{ field.form }}"{% endif %}
|
||||
{% if field.autocomplete is defined %}autocomplete="{{ field.autocomplete }}"{% endif %}
|
||||
{% if field.key %}
|
||||
data-key-observe="{{ (scope ~ field.name)|fieldName }}"
|
||||
{% endif %}
|
||||
{% if field.datasets %}
|
||||
{% for datakey, datavalue in field.datasets %}
|
||||
data-{{ datakey }}="{{ datavalue|e('html_attr') }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if field.attributes %}
|
||||
{% for key, value in field.attributes %}
|
||||
{{ key }}="{{ value|e('html_attr') }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if field.placeholder %}<option value="" disabled selected>{{ field.placeholder|t|raw }}</option>{% endif %}
|
||||
|
||||
{% set options = field.options %}
|
||||
{% if field.selectize.create and value %}
|
||||
{% set custom_value = field.multiple ? value : { (value): value } %}
|
||||
{% set options = options|merge(custom_value|default([]))|array_unique %}
|
||||
{% endif %}
|
||||
|
||||
{% set value = value is iterable ? value : value|string %}
|
||||
{% for key, item_value in options %}
|
||||
{% if item_value is iterable and item_value.value %}
|
||||
{% set akey = field.selectize and field.multiple ? item_value : key %}
|
||||
{% set avalue = item_value.value|t %}
|
||||
<option {{ item_value.disabled ? 'disabled="disabled"' : '' }}
|
||||
{{ item_value.selected or key == value ? 'selected="selected"' : '' }}
|
||||
{{ item_value.label ? 'label=' ~ item_value.label : '' }}
|
||||
value="{{ akey }}"
|
||||
>
|
||||
{{ avalue|raw }}
|
||||
</option>
|
||||
{% elseif item_value is iterable %}
|
||||
{% set optgroup_label = item_value|keys|first %}
|
||||
<optgroup label="{{ optgroup_label|t|e('html_attr') }}">
|
||||
{% for subkey, suboption in field.options[key][optgroup_label] %}
|
||||
{% set subkey = subkey|string %}
|
||||
{% set item_value = (field.selectize and field.multiple ? suboption : subkey)|string %}
|
||||
{% set selected = (field.selectize ? suboption : subkey)|string %}
|
||||
<option {% if subkey is same as (value) or (field.multiple and selected in value) %}selected="selected"{% endif %} value="{{ subkey }}">
|
||||
{{ suboption|t|raw }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% else %}
|
||||
{% set val = (field.selectize and field.multiple ? item_value : key)|string %}
|
||||
{% set selected = (field.selectize ? item_value : key)|string %}
|
||||
<option {% if val is same as (value) or (field.multiple and selected in value) %}selected="selected"{% endif %} value="{{ val }}">{{ item_value|t|raw }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,2 @@
|
||||
{# Deprecated Form 4.0: Just use `select` field #}
|
||||
{% extends "forms/fields/select/select.html.twig" %}
|
||||
@@ -0,0 +1,105 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block prepend %}
|
||||
<div id="signature-pad" class="signature-pad">
|
||||
<div class="signature-pad--body">
|
||||
<canvas></canvas>
|
||||
</div>
|
||||
<div class="signature-pad--footer">
|
||||
<div class="description">Sign above</div>
|
||||
|
||||
<div class="signature-pad--actions">
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary-border" data-action="clear">Clear Signature</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url('plugin://form/assets/signature_pad.js') }}"></script>
|
||||
<script>
|
||||
|
||||
var wrapper = document.getElementById("signature-pad");
|
||||
var clearButton = wrapper.querySelector("[data-action=clear]");
|
||||
|
||||
var canvas = wrapper.querySelector("canvas");
|
||||
var signaturePad = new SignaturePad(canvas, {
|
||||
// It's Necessary to use an opaque color when saving image as JPEG;
|
||||
// this option can be omitted if only saving as PNG or SVG
|
||||
backgroundColor: 'rgb(255, 255, 255)',
|
||||
onEnd: function() {
|
||||
var input = document.querySelector('[name="data[{{ field.name }}]"]');
|
||||
input.value = this.toDataURL();
|
||||
}
|
||||
});
|
||||
|
||||
// Adjust canvas coordinate space taking into account pixel ratio,
|
||||
// to make it look crisp on mobile devices.
|
||||
// This also causes canvas to be cleared.
|
||||
function resizeCanvas() {
|
||||
// When zoomed out to less than 100%, for some very strange reason,
|
||||
// some browsers report devicePixelRatio as less than 1
|
||||
// and only part of the canvas is cleared then.
|
||||
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||
|
||||
// This part causes the canvas to be cleared
|
||||
canvas.width = canvas.offsetWidth * ratio;
|
||||
canvas.height = canvas.offsetHeight * ratio;
|
||||
canvas.getContext("2d").scale(ratio, ratio);
|
||||
|
||||
// This library does not listen for canvas changes, so after the canvas is automatically
|
||||
// cleared by the browser, SignaturePad#isEmpty might still return false, even though the
|
||||
// canvas looks empty, because the internal data of this library wasn't cleared. To make sure
|
||||
// that the state of this library is consistent with visual state of the canvas, you
|
||||
// have to clear it manually.
|
||||
signaturePad.clear();
|
||||
}
|
||||
|
||||
// On mobile devices it might make more sense to listen to orientation change,
|
||||
// rather than window resize events.
|
||||
window.onresize = resizeCanvas;
|
||||
resizeCanvas();
|
||||
|
||||
function download(dataURL, filename) {
|
||||
var blob = dataURLToBlob(dataURL);
|
||||
var url = window.URL.createObjectURL(blob);
|
||||
|
||||
var a = document.createElement("a");
|
||||
a.style = "display: none";
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// One could simply use Canvas#toBlob method instead, but it's just to show
|
||||
// that it can be done using result of SignaturePad#toDataURL.
|
||||
function dataURLToBlob(dataURL) {
|
||||
// Code taken from https://github.com/ebidel/filer.js
|
||||
var parts = dataURL.split(';base64,');
|
||||
var contentType = parts[0].split(":")[1];
|
||||
var raw = window.atob(parts[1]);
|
||||
var rawLength = raw.length;
|
||||
var uInt8Array = new Uint8Array(rawLength);
|
||||
|
||||
for (var i = 0; i < rawLength; ++i) {
|
||||
uInt8Array[i] = raw.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new Blob([uInt8Array], { type: contentType });
|
||||
}
|
||||
|
||||
clearButton.addEventListener("click", function (event) {
|
||||
signaturePad.clear();
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock prepend %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="hidden"
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
<div class="form-spacer {{ field.classes }}">
|
||||
{% if field.title %}
|
||||
<h3>{{- field.title|t|raw -}}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if field.markdown %}
|
||||
<p>{{- field.text|t|markdown|raw -}}</p>
|
||||
{% else %}
|
||||
<p>{{- field.text|t|raw -}}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if field.underline %}
|
||||
<hr />
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{% extends 'forms/fields/checkbox/checkbox.html.twig' %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
{% embed 'forms/default/fields.html.twig' with {name: field.name, fields: field.fields} %}
|
||||
{% block outer_markup_field_open %}<div class="form-tab">{% endblock %}
|
||||
{% block outer_markup_field_close %}</div>{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
60
user/plugins/form/templates/forms/fields/tabs/tabs.html.twig
Normal file
60
user/plugins/form/templates/forms/fields/tabs/tabs.html.twig
Normal file
@@ -0,0 +1,60 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% if not grav.admin %}
|
||||
{% do assets.addJs('plugin://form/assets/form.vendor.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% do assets.addJs('plugin://form/assets/form.min.js', { 'group': 'bottom', 'loading': 'defer' }) %}
|
||||
{% endif %}
|
||||
|
||||
{% block field %}
|
||||
<div class="form-tabs {{ field.class }} {{ field.classes }}">
|
||||
|
||||
{% set fields = prepare_form_fields(field.fields, field.name) %}
|
||||
{% if fields|length %}
|
||||
{% set tabs = {} %}
|
||||
{% for tab in fields %}
|
||||
{% if tab.type == 'tab' and not tab.validate.ignore and (tab.security is empty or authorize(array(tab.security))) %}
|
||||
{% set tabs = tabs|merge([tab]) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% set count = tabs|length %}
|
||||
|
||||
{% if count == 0 %}
|
||||
{# Nothing to display #}
|
||||
{% elseif count == 1 and not admin %}
|
||||
{% set fields = (tabs|first).fields %}
|
||||
{% for field in fields %}
|
||||
{% set value = field.name ? (form ? form.value(field.name) : data.value(field.name)) : data.toArray %}
|
||||
{% set field_templates = include_form_field(field.type, field_layout, 'text') %}
|
||||
{% include field_templates %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% set tabsKey = form.name ~ '-' ~ fields|keys|join(':')|md5 %}
|
||||
{% set storedValue = grav.admin ? get_cookie('grav-tabs-state')|default('{}')|json_decode : [] %}
|
||||
{% set storedTab = attribute(storedValue, 'tab-' ~ tabsKey) %}
|
||||
{% if storedTab is empty %}
|
||||
{% set active = uri.params.tab ?? field.active ?? 1 %}
|
||||
{% endif %}
|
||||
|
||||
<div class="tabs-nav">
|
||||
{% for tab in tabs %}
|
||||
{% if tab.type == 'tab' and (tab.condition is null or tab.condition == true) %}
|
||||
<a class="tab__link {{ (storedTab == scope ~ tab.name) or active == loop.index ? 'active' : '' }}" data-tabid="tab-{{ tabsKey ~ loop.index }}" data-tabkey="tab-{{ tabsKey }}" data-scope="{{ scope ~ tab.name }}">
|
||||
<span>{{ tab.title|t }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="tabs-content">
|
||||
{% embed 'forms/default/fields.html.twig' with {name: field.name, fields: fields} %}
|
||||
{% block inner_markup_field_open %}
|
||||
<div id="tab-{{ tabsKey ~ loop.index }}" class="tab__content {{ (storedTab == scope ~ field.name) or active == loop.index ? 'active' : '' }}">
|
||||
{% endblock %}
|
||||
{% block inner_markup_field_close %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,9 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="tel"
|
||||
{% if field.size %}size="{{ field.size }}"{% endif %}
|
||||
{% if field.minlength is defined or field.validate.min is defined %}minlength="{{ field.minlength | default(field.validate.min) }}"{% endif %}
|
||||
{% if field.maxlength is defined or field.validate.max is defined %}maxlength="{{ field.maxlength | default(field.validate.max) }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% set value = iterable ? value|join(', ') : value|string %}
|
||||
|
||||
{% if link -%}
|
||||
<a href="{{ link|e }}">{{ value|e }}</a>
|
||||
{%- else -%}
|
||||
{{ value|e }}
|
||||
{%- endif %}
|
||||
39
user/plugins/form/templates/forms/fields/text/text.html.twig
Normal file
39
user/plugins/form/templates/forms/fields/text/text.html.twig
Normal file
@@ -0,0 +1,39 @@
|
||||
{% if field.prepend or field.append or field.copy_to_clipboard %}
|
||||
{% set field = field|merge({'wrapper_classes': 'form-input-addon-wrapper'}) %}
|
||||
{% endif %}
|
||||
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block prepend %}
|
||||
{% if field.prepend %}
|
||||
<div class="form-input-addon form-input-prepend">
|
||||
{{- field.prepend|t|raw -}}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="text"
|
||||
{% if field.size %}size="{{ field.size }}"{% endif %}
|
||||
{% if field.minlength is defined or field.validate.min is defined %}minlength="{{ field.minlength | default(field.validate.min) }}"{% endif %}
|
||||
{% if field.maxlength is defined or field.validate.max is defined %}maxlength="{{ field.maxlength | default(field.validate.max) }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block append %}
|
||||
{% if field.copy_to_clipboard %}
|
||||
<div class="form-input-addon form-input-append copy-to-clipboard">
|
||||
{% if field.copy_to_clipboard in ['0', '1'] %}
|
||||
<i class="fa fa-clipboard"></i>
|
||||
{% else %}
|
||||
{{- field.copy_to_clipboard|t|raw -}}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elseif field.append %}
|
||||
<div class="form-input-addon form-input-append">
|
||||
{{- field.append|t|raw -}}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input %}
|
||||
<div class="{{ form_field_wrapper_classes ?: 'form-textarea-wrapper' }} {{ field.size }} {{ field.wrapper_classes }}">
|
||||
{% block prepend %}{% endblock prepend %}
|
||||
<textarea
|
||||
{# required attribute structures #}
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
{# input attribute structures #}
|
||||
{% block input_attributes %}
|
||||
class="{{ form_field_textarea_classes }} {{ field.classes }} {{ field.size }}"
|
||||
{% if field.id is defined %}id="{{ field.id|e }}" {% endif %}
|
||||
{% if field.style is defined %}style="{{ field.style|e }}" {% endif %}
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if field.placeholder %}placeholder="{{ field.placeholder|t }}"{% endif %}
|
||||
{% if field.autofocus in ['on', 'true', 1] %}autofocus="autofocus"{% endif %}
|
||||
{% if field.novalidate in ['on', 'true', 1] %}novalidate="novalidate"{% endif %}
|
||||
{% if field.readonly in ['on', 'true', 1] %}readonly="readonly"{% endif %}
|
||||
{% if field.autocomplete in ['on', 'off'] %}autocomplete="{{ field.autocomplete }}"{% endif %}
|
||||
{% if field.tabindex %}tabindex="{{ field.tabindex }}"{% endif %}
|
||||
{% if required %}required="required"{% endif %}
|
||||
{% if field.validate.pattern %}pattern="{{ field.validate.pattern }}"{% endif %}
|
||||
{% if field.validate.message %}title="{{ field.validate.message|t|e }}"{% endif %}
|
||||
{% if field.rows is defined %}rows="{{ field.rows }}"{% endif %}
|
||||
{% if field.cols is defined %}cols="{{ field.cols }}"{% endif %}
|
||||
{% if field.minlength is defined or field.validate.min is defined %}minlength="{{ field.minlength | default(field.validate.min) }}"{% endif %}
|
||||
{% if field.maxlength is defined or field.validate.max is defined %}maxlength="{{ field.maxlength | default(field.validate.max) }}"{% endif %}
|
||||
{% if field.datasets %}
|
||||
{% for datakey, datavalue in field.datasets %}
|
||||
data-{{ datakey }}="{{ datavalue|e('html_attr') }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if field.attributes is defined %}
|
||||
{% for key,attribute in field.attributes %}
|
||||
{% if attribute|of_type('array') %}
|
||||
{{ attribute.name }}="{{ attribute.value|e('html_attr') }}"
|
||||
{% else %}
|
||||
{{ key }}="{{ attribute|e('html_attr') }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
>{{ value|trim|e('html') }}</textarea>
|
||||
{% block append %}{% endblock append %}
|
||||
{% if inline_errors and errors %}
|
||||
<div class="{{ form_errors_classes ?: 'form-errors' }}">
|
||||
<p class="form-message"><i class="fa fa-exclamation-circle"></i> {{ errors|first }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{{ value ? value|date('g:i A')|e }}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user