martin plugin
This commit is contained in:
parent
17e2de75ea
commit
23e4015d6c
|
|
@ -0,0 +1,19 @@
|
|||
# MIT license
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms;
|
||||
|
||||
use Backend, Lang, Validator;
|
||||
use System\Classes\PluginBase;
|
||||
use System\Classes\SettingsManager;
|
||||
use Martin\Forms\Classes\BackendHelpers;
|
||||
use Martin\Forms\Classes\GDPR;
|
||||
use Martin\Forms\Classes\ReCaptchaValidator;
|
||||
use Martin\Forms\Classes\UnreadRecords;
|
||||
use Martin\Forms\Models\Settings;
|
||||
|
||||
class Plugin extends PluginBase {
|
||||
|
||||
public function pluginDetails() {
|
||||
return [
|
||||
'name' => 'martin.forms::lang.plugin.name',
|
||||
'description' => 'martin.forms::lang.plugin.description',
|
||||
'author' => 'Martin M.',
|
||||
'icon' => 'icon-bolt',
|
||||
'homepage' => 'https://github.com/skydiver/'
|
||||
];
|
||||
}
|
||||
|
||||
public function registerNavigation() {
|
||||
if(Settings::get('global_hide_button', false)) { return; }
|
||||
return [
|
||||
'forms' => [
|
||||
'label' => 'martin.forms::lang.menu.label',
|
||||
'icon' => 'icon-bolt',
|
||||
'iconSvg' => 'plugins/martin/forms/assets/imgs/icon.svg',
|
||||
'url' => BackendHelpers::getBackendURL(['martin.forms.access_records' => 'martin/forms/records', 'martin.forms.access_exports' => 'martin/forms/exports'], 'martin.forms.access_records'),
|
||||
'permissions' => ['martin.forms.*'],
|
||||
'sideMenu' => [
|
||||
'records' => [
|
||||
'label' => 'martin.forms::lang.menu.records.label',
|
||||
'icon' => 'icon-database',
|
||||
'url' => Backend::url('martin/forms/records'),
|
||||
'permissions' => ['martin.forms.access_records'],
|
||||
'counter' => UnreadRecords::getTotal(),
|
||||
'counterLabel' => 'Un-Read Messages'
|
||||
],
|
||||
'exports' => [
|
||||
'label' => 'martin.forms::lang.menu.exports.label',
|
||||
'icon' => 'icon-download',
|
||||
'url' => Backend::url('martin/forms/exports'),
|
||||
'permissions' => ['martin.forms.access_exports']
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function registerSettings() {
|
||||
return [
|
||||
'config' => [
|
||||
'label' => 'martin.forms::lang.menu.label',
|
||||
'description' => 'martin.forms::lang.menu.settings',
|
||||
'category' => SettingsManager::CATEGORY_CMS,
|
||||
'icon' => 'icon-bolt',
|
||||
'class' => 'Martin\Forms\Models\Settings',
|
||||
'permissions' => ['martin.forms.access_settings'],
|
||||
'order' => 500
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function registerPermissions() {
|
||||
return [
|
||||
'martin.forms.access_settings' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.access_settings'],
|
||||
'martin.forms.access_records' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.access_records'],
|
||||
'martin.forms.access_exports' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.access_exports'],
|
||||
'martin.forms.gdpr_cleanup' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.gdpr_cleanup'],
|
||||
];
|
||||
}
|
||||
|
||||
public function registerComponents() {
|
||||
return [
|
||||
'Martin\Forms\Components\GenericForm' => 'genericForm',
|
||||
'Martin\Forms\Components\UploadForm' => 'uploadForm',
|
||||
'Martin\Forms\Components\EmptyForm' => 'emptyForm',
|
||||
];
|
||||
}
|
||||
|
||||
public function registerMailTemplates() {
|
||||
return [
|
||||
'martin.forms::mail.notification' => Lang::get('martin.forms::lang.mails.form_notification.description'),
|
||||
'martin.forms::mail.autoresponse' => Lang::get('martin.forms::lang.mails.form_autoresponse.description'),
|
||||
];
|
||||
}
|
||||
|
||||
public function register() {
|
||||
$this->app->resolving('validator', function($validator) {
|
||||
Validator::extend('recaptcha', 'Martin\Forms\Classes\ReCaptchaValidator@validateReCaptcha');
|
||||
});
|
||||
}
|
||||
|
||||
public function registerSchedule($schedule) {
|
||||
$schedule->call(function () {
|
||||
$records = GDPR::cleanRecords();
|
||||
})->daily();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# Magic Forms for OctoberCMS
|
||||
Create easy (and almost magic) AJAX forms.
|
||||
|
||||
|
||||
|
||||
## Why Magic Forms?
|
||||
Almost everyday we do forms for our clients, personal projects, etc
|
||||
|
||||
Sometimes we need to add or remove fields, change validations, store data and at some point, this can be boring and repetitive.
|
||||
|
||||
So, the objective was to find a way to just put the HTML elements on the page, skip the repetitive task of coding and (with some kind of magic) store this data on a database or send by mail.
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
* Create any type of form: contact, feedback, registration, uploads, etc
|
||||
* Write only HTML
|
||||
* Don't code forms logic
|
||||
* Laravel validation
|
||||
* Custom validation errors
|
||||
* Use multiple forms on same page
|
||||
* Store on database
|
||||
* Export data in CSV
|
||||
* Access database records from backend
|
||||
* Send mail notifications to multiple recipients
|
||||
* Auto-response email on form submit
|
||||
* reCAPTCHA validation
|
||||
* Support for Translate plugin
|
||||
* Inline errors with fields (read documentation for more info)
|
||||
* AJAX file uploads (BETA, available since v1.3.0)
|
||||
|
||||
|
||||
|
||||
## Documentation
|
||||
Checkout our docs at:
|
||||
> https://skydiver.github.io/october-plugin-forms/
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
.files-container ul { padding:0; list-style:none; }
|
||||
|
||||
.record-table { width:100%; border-collapse:collapse; box-shadow:5px 5px 10px #AAA; }
|
||||
.record-table td { padding:10px; border:solid 1px #D4D8DA; }
|
||||
.record-label { width:20%; background-color:#ECF0F1; text-align:right; }
|
||||
.record-value { width:80%; background-color:#F9F9F9; }
|
||||
.record-value ul { margin:0; }
|
||||
.record-metadata { font-size:0.8em; color:#888; font-weight:bold; }
|
||||
|
|
@ -0,0 +1,704 @@
|
|||
.responsiv-uploader-fileupload:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object {
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .icon-container {
|
||||
display: table;
|
||||
opacity: .6;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .icon-container i {
|
||||
color: #95a5a6;
|
||||
display: inline-block;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .icon-container div {
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .icon-container.image > div.icon-wrapper {
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object h4 {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #2b3e50;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 150%;
|
||||
margin: 15px 0 5px 0;
|
||||
padding-right: 0;
|
||||
transition: padding 0.1s;
|
||||
position: relative;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object h4 a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
font-weight: 400;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object p.size,
|
||||
.responsiv-uploader-fileupload .upload-object p.error {
|
||||
font-size: 12px;
|
||||
color: #95a5a6;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object p.size strong,
|
||||
.responsiv-uploader-fileupload .upload-object p.error strong {
|
||||
font-weight: 400;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object p.error {
|
||||
display: none;
|
||||
color: #ab2a1c;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .info h4 a,
|
||||
.responsiv-uploader-fileupload .upload-object .meta a.upload-remove-button {
|
||||
color: #2b3e50;
|
||||
display: none;
|
||||
font-size: 24px;
|
||||
line-height: 16px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .icon-container {
|
||||
position: relative;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object .icon-container:after {
|
||||
background-image: url('../../../../../modules/system/assets/ui/images/loader-transparent.svg');
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -20px;
|
||||
margin-left: -20px;
|
||||
display: block;
|
||||
background-size: 40px 40px;
|
||||
background-position: 50% 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-success .icon-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-success .icon-container:after {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-loading .icon-container {
|
||||
opacity: .6;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-loading .icon-container:after {
|
||||
opacity: 1;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-success {
|
||||
cursor: pointer;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-success .progress-bar {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-success:hover h4 a,
|
||||
.responsiv-uploader-fileupload .upload-object.is-success:hover .meta .upload-remove-button {
|
||||
display: block;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error {
|
||||
cursor: pointer;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .progress-bar {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .icon-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .icon-container > img,
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .icon-container > i {
|
||||
opacity: .5;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .info h4 {
|
||||
color: #ab2a1c;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error p.error {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .info .upload-remove-button,
|
||||
.responsiv-uploader-fileupload .upload-object.is-error .meta .upload-remove-button {
|
||||
display: block;
|
||||
}
|
||||
.responsiv-uploader-fileupload.is-preview .upload-button,
|
||||
.responsiv-uploader-fileupload.is-preview .upload-remove-button {
|
||||
display: none !important;
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.responsiv-uploader-fileupload .upload-object.is-success h4 a,
|
||||
.responsiv-uploader-fileupload .upload-object.is-success .meta .upload-remove-button {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
@-moz-keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-moz-transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes spin {
|
||||
0% {
|
||||
-o-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-o-transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@-ms-keyframes spin {
|
||||
0% {
|
||||
-ms-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-ms-transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-button,
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object {
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object {
|
||||
background: #fff;
|
||||
border: 1px solid #ecf0f1;
|
||||
width: 260px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .progress-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 5px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .progress-bar .upload-progress {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
line-height: 5px;
|
||||
color: #ffffff;
|
||||
background-color: #5fb6f5;
|
||||
box-shadow: none;
|
||||
transition: width .6s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .icon-container {
|
||||
border-right: 1px solid #f6f8f9;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .icon-container i {
|
||||
font-size: 35px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .icon-container.image img {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
width: auto;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .info {
|
||||
margin-left: 90px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .info h4 {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .info h4 a {
|
||||
right: 15px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object .meta {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 15px 0 90px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object.upload-placeholder {
|
||||
height: 75px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object.upload-placeholder:after {
|
||||
opacity: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover {
|
||||
background: #4da7e8 !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover i,
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover p.size,
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover p.error,
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover .upload-remove-button {
|
||||
color: #ecf0f1;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover h4 {
|
||||
color: white;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover .icon-container {
|
||||
border-right-color: #4da7e8 !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover.is-error {
|
||||
background: #ab2a1c !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object:hover h4 {
|
||||
padding-right: 35px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object.is-error h4 {
|
||||
padding-right: 35px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object.is-error .info p.size {
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object.is-error .info p.error {
|
||||
padding-bottom: 11px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi.is-preview .upload-files-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object {
|
||||
width: 230px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-button {
|
||||
width: 100%;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-files-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-multi .upload-object {
|
||||
margin-right: 0;
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single.is-populated .upload-button {
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button {
|
||||
display: block;
|
||||
float: left;
|
||||
border: 2px dotted rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
outline: none;
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button .upload-button-icon {
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -11px;
|
||||
margin-left: -11px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button .upload-button-icon:before {
|
||||
content: "+";
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-size: 22px;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
line-height: 22px;
|
||||
color: rgba(0, 0, 0, 0.1);
|
||||
font-weight: 700;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button:hover {
|
||||
border: 2px dotted rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button:hover .upload-button-icon:before {
|
||||
color: #5cb85c;
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button:focus {
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
background: transparent;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-button:focus .upload-button-icon:before {
|
||||
color: #5cb85c;
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object {
|
||||
padding-bottom: 66px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object .icon-container {
|
||||
border: 1px solid #f6f8f9;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object .icon-container.image img {
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object .progress-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 5px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object .progress-bar .upload-progress {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
line-height: 5px;
|
||||
color: #ffffff;
|
||||
background-color: #5fb6f5;
|
||||
box-shadow: none;
|
||||
transition: width .6s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object .info {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 66px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object .meta {
|
||||
position: absolute;
|
||||
bottom: 65px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 15px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object:hover h4 {
|
||||
padding-right: 20px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object.is-error h4 {
|
||||
padding-right: 20px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object.is-error .info p.size {
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object.is-error .info p.error {
|
||||
padding-bottom: 11px;
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.responsiv-uploader-fileupload.style-image-single .upload-object h4 {
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-files-container {
|
||||
border: 1px solid #eeeeee;
|
||||
border-radius: 3px;
|
||||
border-bottom: none;
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi.is-populated .upload-files-container {
|
||||
display: block;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:nth-child(even) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .icon-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
width: 35px;
|
||||
padding: 11px 7px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info {
|
||||
margin-left: 35px;
|
||||
margin-right: 15%;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info h4,
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info p {
|
||||
margin: 0;
|
||||
padding: 11px 0;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 150%;
|
||||
color: #666666;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info h4 {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info h4 a {
|
||||
padding: 10px 0;
|
||||
right: 15px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info p.size {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 15%;
|
||||
display: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .info p.error {
|
||||
color: #ab2a1c;
|
||||
padding-top: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .progress-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 5px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .progress-bar .upload-progress {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
line-height: 5px;
|
||||
color: #ffffff;
|
||||
background-color: #5fb6f5;
|
||||
box-shadow: none;
|
||||
transition: width .6s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .meta {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-right: 15px;
|
||||
width: 15%;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .meta .upload-remove-button {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
right: 0;
|
||||
bottom: auto;
|
||||
line-height: 150%;
|
||||
padding: 10px 0;
|
||||
z-index: 100;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object .icon-container:after {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
margin-left: -10px;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object.is-success .info p.size {
|
||||
display: block;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover {
|
||||
background: #4da7e8 !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover i,
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover p.size,
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover p.error,
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover .upload-remove-button {
|
||||
color: #ecf0f1;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover h4 {
|
||||
color: white;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover .icon-container {
|
||||
border-right-color: #4da7e8 !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover.is-error {
|
||||
background: #ab2a1c !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object:hover h4 {
|
||||
padding-right: 35px;
|
||||
}
|
||||
@media (max-width: 1199px) {
|
||||
.responsiv-uploader-fileupload.style-file-multi .info {
|
||||
margin-right: 20% !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .info p.size {
|
||||
width: 20% !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .meta {
|
||||
width: 20% !important;
|
||||
}
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.responsiv-uploader-fileupload.style-file-multi .upload-object h4 {
|
||||
padding-right: 35px !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .info {
|
||||
margin-right: 25% !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .info p.size {
|
||||
width: 25% !important;
|
||||
padding-right: 35px !important;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-multi .meta {
|
||||
width: 25% !important;
|
||||
}
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single {
|
||||
background-color: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding-right: 11px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -44px;
|
||||
height: 88px;
|
||||
right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-empty-message {
|
||||
padding: 10px 0 10px 11px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single.is-populated .upload-button,
|
||||
.responsiv-uploader-fileupload.style-file-single.is-populated .upload-empty-message {
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 0 10px 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .icon-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 35px;
|
||||
padding: 0 5px;
|
||||
margin: 8px 0 0 7px;
|
||||
text-align: center;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .info {
|
||||
margin-left: 54px;
|
||||
margin-right: 15%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .info h4,
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .info p {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
line-height: 150%;
|
||||
color: #666666;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .info p.size {
|
||||
font-weight: normal;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .info p.size:before {
|
||||
content: " - ";
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .info p.error {
|
||||
color: #ab2a1c;
|
||||
padding-top: 0;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .progress-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 5px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -2px;
|
||||
right: 5px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .progress-bar .upload-progress {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
line-height: 5px;
|
||||
color: #ffffff;
|
||||
background-color: #5fb6f5;
|
||||
box-shadow: none;
|
||||
transition: width .6s ease;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .meta {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -44px;
|
||||
height: 88px;
|
||||
right: 0;
|
||||
width: 15%;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .meta .upload-remove-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: -10px;
|
||||
margin-right: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object .icon-container:after {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
margin-left: -10px;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object.is-error .info p.size {
|
||||
display: none;
|
||||
}
|
||||
.responsiv-uploader-fileupload.style-file-single .upload-object.is-error .info p.error:before {
|
||||
content: " - ";
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
xml:space="preserve"
|
||||
x="0px"
|
||||
y="0px"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="noun_9594.svg"
|
||||
width="64"
|
||||
height="64"><metadata
|
||||
id="metadata10"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs8"><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4168"><stop
|
||||
id="stop4172"
|
||||
offset="0"
|
||||
style="stop-color:#f89c3a;stop-opacity:1" /><stop
|
||||
id="stop4170"
|
||||
offset="1"
|
||||
style="stop-color:#fec503;stop-opacity:1" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4168"
|
||||
id="linearGradient4174"
|
||||
x1="38.347958"
|
||||
y1="1.3644062"
|
||||
x2="36.229313"
|
||||
y2="99.245766"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.63737476,0,0,0.63737476,7.0795147,0.12478476)" /></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1863"
|
||||
inkscape:window-height="1056"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.675088"
|
||||
inkscape:cx="34.430274"
|
||||
inkscape:cy="54.352813"
|
||||
inkscape:window-x="57"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" /><path
|
||||
d="m 26.660202,0.31923857 24.57674,0.062718 -15.93437,22.35342543 15.394221,0 -37.000145,41.051255 13.233628,-28.357774 -14.313925,0 z"
|
||||
style="fill:url(#linearGradient4174);fill-opacity:1;stroke:#fe8f00;stroke-width:0.63737476;stroke-opacity:1"
|
||||
id="path4"
|
||||
inkscape:connector-curvature="0" /></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
|
|
@ -0,0 +1,7 @@
|
|||
$(window).on('ajaxInvalidField', function(event, fieldElement, fieldName, errorMsg, isFirst) {
|
||||
$(fieldElement).closest('.form-group').addClass('has-error');
|
||||
});
|
||||
|
||||
$(document).on('ajaxPromise', '[data-request]', function() {
|
||||
$(this).closest('form').find('.form-group.has-error').removeClass('has-error');
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
var captchas = [];
|
||||
|
||||
var onloadCallback = function() {
|
||||
jQuery('.g-recaptcha').each(function(index, el) {
|
||||
captchas[el.id] = grecaptcha.render(el, $(el).data());
|
||||
});
|
||||
}
|
||||
|
||||
function resetReCaptcha(id) {
|
||||
var widget = captchas[id];
|
||||
grecaptcha.reset(widget);
|
||||
}
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* File upload form field control
|
||||
*
|
||||
* Data attributes:
|
||||
* - data-control="fileupload" - enables the file upload plugin
|
||||
* - data-unique-id="XXX" - an optional identifier for multiple uploaders on the same page, this value will
|
||||
* appear in the postback variable called X_OCTOBER_FILEUPLOAD
|
||||
* - data-template - a Dropzone.js template to use for each item
|
||||
*
|
||||
* JavaScript API:
|
||||
* $('div').fileUploader()
|
||||
*
|
||||
* Dependancies:
|
||||
* - Dropzone.js
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// FILEUPLOAD CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var FileUpload = function (element, options) {
|
||||
this.$el = $(element)
|
||||
this.options = options || {}
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
FileUpload.prototype.init = function() {
|
||||
if (this.options.isMulti === null) {
|
||||
this.options.isMulti = this.$el.hasClass('is-multi')
|
||||
}
|
||||
|
||||
if (this.options.isPreview === null) {
|
||||
this.options.isPreview = this.$el.hasClass('is-preview')
|
||||
}
|
||||
|
||||
this.$uploadButton = $('.upload-button', this.$el)
|
||||
this.$filesContainer = $('.upload-files-container', this.$el)
|
||||
this.uploaderOptions = {}
|
||||
|
||||
this.$el.on('click', '.upload-object.is-success', $.proxy(this.onClickSuccessObject, this))
|
||||
this.$el.on('click', '.upload-object.is-error', $.proxy(this.onClickErrorObject, this))
|
||||
|
||||
// Stop here for preview mode
|
||||
if (this.options.isPreview)
|
||||
return
|
||||
|
||||
this.$el.on('click', '.upload-remove-button', $.proxy(this.onRemoveObject, this))
|
||||
|
||||
this.bindUploader()
|
||||
}
|
||||
|
||||
FileUpload.prototype.dispose = function() {
|
||||
|
||||
this.$el.off('click', '.upload-object.is-success', $.proxy(this.onClickSuccessObject, this))
|
||||
this.$el.off('click', '.upload-object.is-error', $.proxy(this.onClickErrorObject, this))
|
||||
this.$el.off('click', '.upload-remove-button', $.proxy(this.onRemoveObject, this))
|
||||
|
||||
this.$el.removeData('oc.fileUpload')
|
||||
|
||||
this.$el = null
|
||||
this.$uploadButton = null
|
||||
this.$filesContainer = null
|
||||
this.uploaderOptions = null
|
||||
|
||||
// In some cases options could contain callbacks,
|
||||
// so it's better to clean them up too.
|
||||
this.options = null
|
||||
}
|
||||
|
||||
//
|
||||
// Uploading
|
||||
//
|
||||
|
||||
FileUpload.prototype.bindUploader = function() {
|
||||
this.uploaderOptions = {
|
||||
url: this.options.url,
|
||||
paramName: this.options.paramName,
|
||||
clickable: this.$uploadButton.get(0),
|
||||
previewsContainer: this.$filesContainer.get(0),
|
||||
maxFiles: !this.options.isMulti ? 1 : null,
|
||||
headers: {}
|
||||
}
|
||||
|
||||
if (this.options.fileTypes) {
|
||||
this.uploaderOptions.acceptedFiles = this.options.fileTypes
|
||||
}
|
||||
|
||||
if (this.options.template) {
|
||||
this.uploaderOptions.previewTemplate = $(this.options.template).html()
|
||||
}
|
||||
|
||||
if (this.options.uniqueId) {
|
||||
this.uploaderOptions.headers['X-OCTOBER-FILEUPLOAD'] = this.options.uniqueId
|
||||
}
|
||||
|
||||
this.uploaderOptions.thumbnailWidth = this.options.thumbnailWidth
|
||||
? this.options.thumbnailWidth : null
|
||||
|
||||
this.uploaderOptions.thumbnailHeight = this.options.thumbnailHeight
|
||||
? this.options.thumbnailHeight : null
|
||||
|
||||
this.uploaderOptions.resize = this.onResizeFileInfo
|
||||
|
||||
/*
|
||||
* Add CSRF token to headers
|
||||
*/
|
||||
var token = $('meta[name="csrf-token"]').attr('content')
|
||||
if (token) {
|
||||
this.uploaderOptions.headers['X-CSRF-TOKEN'] = token
|
||||
}
|
||||
|
||||
this.dropzone = new Dropzone(this.$el.get(0), this.uploaderOptions)
|
||||
this.dropzone.on('addedfile', $.proxy(this.onUploadAddedFile, this))
|
||||
this.dropzone.on('sending', $.proxy(this.onUploadSending, this))
|
||||
this.dropzone.on('success', $.proxy(this.onUploadSuccess, this))
|
||||
this.dropzone.on('error', $.proxy(this.onUploadError, this))
|
||||
|
||||
var _this = this;
|
||||
|
||||
this.dropzone.on("queuecomplete", function (file) {
|
||||
if (this.getUploadingFiles().length === 0 && this.getQueuedFiles().length === 0) {
|
||||
$.event.trigger({
|
||||
uploader: _this,
|
||||
type : "uploadfinished",
|
||||
message : "Dropzone Files upload finished",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.dropzone.on("sending", function (file) {
|
||||
$.event.trigger({
|
||||
uploader: _this,
|
||||
type : "uploadstarted",
|
||||
message : "Dropzone File upload started",
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
FileUpload.prototype.onResizeFileInfo = function(file) {
|
||||
var info,
|
||||
targetWidth,
|
||||
targetHeight
|
||||
|
||||
if (!this.options.thumbnailWidth && !this.options.thumbnailWidth) {
|
||||
targetWidth = targetHeight = 100
|
||||
}
|
||||
else if (this.options.thumbnailWidth) {
|
||||
targetWidth = this.options.thumbnailWidth
|
||||
targetHeight = this.options.thumbnailWidth * file.height / file.width
|
||||
}
|
||||
else if (this.options.thumbnailHeight) {
|
||||
targetWidth = this.options.thumbnailHeight * file.height / file.width
|
||||
targetHeight = this.options.thumbnailHeight
|
||||
}
|
||||
|
||||
// drawImage(image, srcX, srcY, srcWidth, srcHeight, trgX, trgY, trgWidth, trgHeight) takes an image, clips it to
|
||||
// the rectangle (srcX, srcY, srcWidth, srcHeight), scales it to dimensions (trgWidth, trgHeight), and draws it
|
||||
// on the canvas at coordinates (trgX, trgY).
|
||||
info = {
|
||||
srcX: 0,
|
||||
srcY: 0,
|
||||
srcWidth: file.width,
|
||||
srcHeight: file.height,
|
||||
trgX: 0,
|
||||
trgY: 0,
|
||||
trgWidth: targetWidth,
|
||||
trgHeight: targetHeight
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
FileUpload.prototype.onUploadAddedFile = function(file) {
|
||||
var $object = $(file.previewElement).data('dzFileObject', file)
|
||||
|
||||
// Remove any exisiting objects for single variety
|
||||
if (!this.options.isMulti) {
|
||||
this.removeFileFromElement($object.siblings())
|
||||
}
|
||||
|
||||
this.evalIsPopulated()
|
||||
}
|
||||
|
||||
FileUpload.prototype.onUploadSending = function(file, xhr, formData) {
|
||||
this.addExtraFormData(formData)
|
||||
}
|
||||
|
||||
FileUpload.prototype.onUploadSuccess = function(file, response) {
|
||||
var $preview = $(file.previewElement),
|
||||
$img = $('.image img', $preview)
|
||||
|
||||
$preview.addClass('is-success')
|
||||
|
||||
if (response.id) {
|
||||
$preview.data('id', response.id)
|
||||
$preview.data('path', response.path)
|
||||
$('.upload-remove-button', $preview).data('request-data', { file_id: response.id })
|
||||
$img.attr('src', response.thumb)
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger change event (Compatability with october.form.js)
|
||||
*/
|
||||
this.$el.closest('[data-field-name]').trigger('change.oc.formwidget')
|
||||
}
|
||||
|
||||
FileUpload.prototype.onUploadError = function(file, error) {
|
||||
var $preview = $(file.previewElement)
|
||||
$preview.addClass('is-error')
|
||||
}
|
||||
|
||||
FileUpload.prototype.addExtraFormData = function(formData) {
|
||||
if (this.options.extraData) {
|
||||
$.each(this.options.extraData, function (name, value) {
|
||||
formData.append(name, value)
|
||||
})
|
||||
}
|
||||
|
||||
var $form = this.$el.closest('form')
|
||||
if ($form.length > 0) {
|
||||
$.each($form.serializeArray(), function (index, field) {
|
||||
formData.append(field.name, field.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
FileUpload.prototype.removeFileFromElement = function($element) {
|
||||
var self = this
|
||||
|
||||
$element.each(function() {
|
||||
var $el = $(this),
|
||||
obj = $el.data('dzFileObject')
|
||||
|
||||
if (obj) {
|
||||
self.dropzone.removeFile(obj)
|
||||
}
|
||||
else {
|
||||
$el.remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// User interaction
|
||||
//
|
||||
|
||||
FileUpload.prototype.onRemoveObject = function(ev) {
|
||||
var self = this,
|
||||
$object = $(ev.target).closest('.upload-object')
|
||||
|
||||
$(ev.target)
|
||||
.closest('.upload-remove-button')
|
||||
.one('ajaxPromise', function(){
|
||||
$object.addClass('is-loading')
|
||||
})
|
||||
.one('ajaxDone', function(){
|
||||
self.removeFileFromElement($object)
|
||||
self.evalIsPopulated()
|
||||
})
|
||||
.request()
|
||||
|
||||
ev.stopPropagation()
|
||||
}
|
||||
|
||||
FileUpload.prototype.onClickSuccessObject = function(ev) {
|
||||
// if ($(ev.target).closest('.meta').length) return
|
||||
//
|
||||
// var $target = $(ev.target).closest('.upload-object')
|
||||
// window.open($target.data('path'))
|
||||
}
|
||||
|
||||
FileUpload.prototype.onClickErrorObject = function(ev) {
|
||||
var
|
||||
self = this,
|
||||
$object = $(ev.target).closest('.upload-object'),
|
||||
errorMsg = $('[data-dz-errormessage]', $object).text()
|
||||
|
||||
alert(errorMsg)
|
||||
|
||||
this.removeFileFromElement($object)
|
||||
self.evalIsPopulated()
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
FileUpload.prototype.evalIsPopulated = function() {
|
||||
var isPopulated = !!$('.upload-object', this.$filesContainer).length
|
||||
this.$el.toggleClass('is-populated', isPopulated)
|
||||
|
||||
// Reset maxFiles counter
|
||||
if (!isPopulated) {
|
||||
this.dropzone.removeAllFiles()
|
||||
}
|
||||
}
|
||||
|
||||
FileUpload.DEFAULTS = {
|
||||
url: window.location,
|
||||
uniqueId: null,
|
||||
extraData: {},
|
||||
paramName: 'file_data',
|
||||
fileTypes: null,
|
||||
template: null,
|
||||
isMulti: null,
|
||||
isPreview: null,
|
||||
thumbnailWidth: 120,
|
||||
thumbnailHeight: 120
|
||||
}
|
||||
|
||||
// FILEUPLOAD PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.fileUploader
|
||||
|
||||
$.fn.fileUploader = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.fileUpload')
|
||||
var options = $.extend({}, FileUpload.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.fileUpload', (data = new FileUpload(this, options)))
|
||||
if (typeof option == 'string') data[option].call($this)
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.fileUploader.Constructor = FileUpload
|
||||
|
||||
// FILEUPLOAD NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.fileUploader.noConflict = function () {
|
||||
$.fn.fileUpload = old
|
||||
return this
|
||||
}
|
||||
|
||||
// FILEUPLOAD DATA-API
|
||||
// ===============
|
||||
$(document).render(function () {
|
||||
$('[data-control="fileupload"]').fileUploader()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
||||
|
||||
var uploadDropZones = {};
|
||||
|
||||
$(document).on("uploadstarted", function(event) {
|
||||
var frm = $(event.uploader.dropzone.element).parents('form');
|
||||
frm.find(':submit').prop('disabled', true);
|
||||
});
|
||||
|
||||
var martin;
|
||||
$(document).on("uploadfinished", function(event) {
|
||||
var frm = $(event.uploader.dropzone.element).parents('form');
|
||||
frm.find(':submit').prop('disabled', false);
|
||||
var dz = $(event.uploader.dropzone.element).data('unique-id');
|
||||
uploadDropZones[dz] = event;
|
||||
});
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
.uploader-object-active() {
|
||||
background: @uploader-object-active-bg !important;
|
||||
|
||||
i, p.size, p.error, .upload-remove-button {
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
border-right-color: @uploader-object-active-bg !important;
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
background: @uploader-object-error-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.uploader-progress-bar() {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: @uploader-progress-bar-height;
|
||||
background-color: @uploader-progress-bar-bg;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
|
||||
|
||||
.upload-progress {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
line-height: @uploader-progress-bar-height;
|
||||
color: @uploader-progress-bar-color;
|
||||
background-color: #5fb6f5;
|
||||
box-shadow: none;
|
||||
transition: width .6s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.uploader-block-button() {
|
||||
display: block;
|
||||
float: left;
|
||||
border: 2px dotted rgba(0,0,0,.1);
|
||||
position: relative;
|
||||
outline: none;
|
||||
|
||||
.upload-button-icon {
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -11px;
|
||||
margin-left: -11px;
|
||||
|
||||
&:before {
|
||||
content: "+";
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-size: 22px;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
line-height: 22px;
|
||||
color: rgba(0,0,0,.1);
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 2px dotted rgba(0,0,0,.2);
|
||||
|
||||
.upload-button-icon:before {
|
||||
color: #5cb85c;
|
||||
color: rgba(0,0,0,.2);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 2px solid rgba(0,0,0,.3);
|
||||
background: transparent;
|
||||
|
||||
.upload-button-icon:before {
|
||||
color: #5cb85c;
|
||||
color: rgba(0,0,0,.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.uploader-small-loader() {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
margin-left: -10px;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
.uploader-vertical-align() {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -44px;
|
||||
height: 88px;
|
||||
}
|
||||
|
||||
//
|
||||
// Shared
|
||||
//
|
||||
|
||||
.responsiv-uploader-fileupload {
|
||||
|
||||
// Clearfix
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
//
|
||||
// Uploaded item
|
||||
//
|
||||
|
||||
.upload-object {
|
||||
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: table;
|
||||
opacity: .6;
|
||||
|
||||
i {
|
||||
color: #95a5a6;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div {
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container.image {
|
||||
> div.icon-wrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #2b3e50;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 150%;
|
||||
margin: 15px 0 5px 0;
|
||||
padding-right: 0;
|
||||
transition: padding 0.1s;
|
||||
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
p.size, p.error {
|
||||
font-size: 12px;
|
||||
color: #95a5a6;
|
||||
strong { font-weight: 400; }
|
||||
}
|
||||
|
||||
p.error {
|
||||
display: none;
|
||||
color: #ab2a1c;
|
||||
}
|
||||
|
||||
.meta {}
|
||||
|
||||
.info h4 a,
|
||||
.meta a.upload-remove-button {
|
||||
color: #2b3e50;
|
||||
display: none;
|
||||
font-size: 24px;
|
||||
line-height: 16px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Loading State
|
||||
//
|
||||
|
||||
.upload-object {
|
||||
.icon-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-container:after {
|
||||
background-image: url('../../../../../modules/system/assets/ui/images/loader-transparent.svg');
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -20px;
|
||||
margin-left: -20px;
|
||||
display: block;
|
||||
background-size: 40px 40px;
|
||||
background-position: 50% 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
&.is-success {
|
||||
.icon-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.icon-container:after {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
.icon-container {
|
||||
opacity: .6;
|
||||
}
|
||||
.icon-container:after {
|
||||
opacity: 1;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Success state
|
||||
//
|
||||
|
||||
.upload-object.is-success {
|
||||
cursor: pointer;
|
||||
|
||||
.progress-bar {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
h4 a,
|
||||
.meta .upload-remove-button { display: block; }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Error State
|
||||
//
|
||||
|
||||
.upload-object.is-error {
|
||||
cursor: pointer;
|
||||
|
||||
.progress-bar {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
opacity: 1;
|
||||
> img, > i {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
.info h4 {
|
||||
color: #ab2a1c;
|
||||
}
|
||||
|
||||
p.error {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.info .upload-remove-button,
|
||||
.meta .upload-remove-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Preview mode
|
||||
//
|
||||
|
||||
&.is-preview {
|
||||
.upload-button,
|
||||
.upload-remove-button {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Media
|
||||
//
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.responsiv-uploader-fileupload {
|
||||
.upload-object.is-success {
|
||||
h4 a,
|
||||
.meta .upload-remove-button { display: block !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Spin animation
|
||||
//
|
||||
|
||||
@-moz-keyframes spin {
|
||||
0% { -moz-transform: rotate(0deg); }
|
||||
100% { -moz-transform: rotate(359deg); }
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(359deg); }
|
||||
}
|
||||
@-o-keyframes spin {
|
||||
0% { -o-transform: rotate(0deg); }
|
||||
100% { -o-transform: rotate(359deg); }
|
||||
}
|
||||
@-ms-keyframes spin {
|
||||
0% { -ms-transform: rotate(0deg); }
|
||||
100% { -ms-transform: rotate(359deg); }
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(359deg); }
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// Multi File
|
||||
//
|
||||
|
||||
.responsiv-uploader-fileupload.style-file-multi {
|
||||
.upload-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-files-container {
|
||||
border: 1px solid @uploader-list-border-color;
|
||||
border-radius: 3px;
|
||||
border-bottom: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-populated .upload-files-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.upload-object {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid @uploader-list-border-color;
|
||||
padding-left: 10px;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: @uploader-list-accent-bg;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
width: 35px;
|
||||
padding: 11px 7px;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-left: 35px;
|
||||
margin-right: 15%;
|
||||
|
||||
h4, p {
|
||||
margin: 0;
|
||||
padding: 11px 0;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 150%;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding-right: 15px;
|
||||
|
||||
a {
|
||||
padding: 10px 0;
|
||||
right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
p.size {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 15%;
|
||||
display: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
p.error {
|
||||
color: #ab2a1c;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
.uploader-progress-bar();
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.meta {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-right: 15px;
|
||||
width: 15%;
|
||||
|
||||
.upload-remove-button {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
right: 0;
|
||||
bottom: auto;
|
||||
line-height: 150%;
|
||||
padding: 10px 0;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container:after {
|
||||
.uploader-small-loader();
|
||||
}
|
||||
|
||||
//
|
||||
// Success
|
||||
//
|
||||
|
||||
&.is-success {
|
||||
.info p.size { display: block; }
|
||||
}
|
||||
|
||||
//
|
||||
// Hover
|
||||
//
|
||||
|
||||
&:hover {
|
||||
.uploader-object-active();
|
||||
h4 { padding-right: 35px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Media
|
||||
//
|
||||
|
||||
@media (max-width: @screen-md-max) {
|
||||
.responsiv-uploader-fileupload.style-file-multi {
|
||||
.info {
|
||||
margin-right: 20% !important;
|
||||
p.size {
|
||||
width: 20% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
width: 20% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-sm-max) {
|
||||
.responsiv-uploader-fileupload.style-file-multi {
|
||||
.upload-object {
|
||||
h4 { padding-right: 35px !important; }
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-right: 25% !important;
|
||||
p.size {
|
||||
width: 25% !important;
|
||||
padding-right: 35px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
width: 25% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// Single File
|
||||
//
|
||||
|
||||
.responsiv-uploader-fileupload.style-file-single {
|
||||
background-color: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding-right: 11px;
|
||||
|
||||
.upload-button {
|
||||
.uploader-vertical-align();
|
||||
right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.upload-empty-message {
|
||||
padding: 10px 0 10px 11px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&.is-populated {
|
||||
.upload-button,
|
||||
.upload-empty-message {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-object {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 0 10px 0;
|
||||
|
||||
.icon-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 35px;
|
||||
padding: 0 5px;
|
||||
margin: 8px 0 0 7px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-left: 54px;
|
||||
margin-right: 15%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
h4, p {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
line-height: 150%;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
p.size {
|
||||
font-weight: normal;
|
||||
&:before {
|
||||
content: " - ";
|
||||
}
|
||||
}
|
||||
|
||||
p.error {
|
||||
color: #ab2a1c;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
.uploader-progress-bar();
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -2px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
.uploader-vertical-align();
|
||||
right: 0;
|
||||
width: 15%;
|
||||
.upload-remove-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: -10px;
|
||||
margin-right: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container:after {
|
||||
.uploader-small-loader();
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
.info {
|
||||
p.size { display: none; }
|
||||
p.error:before {
|
||||
content: " - ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// Multi Image
|
||||
//
|
||||
|
||||
.responsiv-uploader-fileupload.style-image-multi {
|
||||
.upload-button,
|
||||
.upload-object {
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.upload-object {
|
||||
background: #fff;
|
||||
border: 1px solid #ecf0f1;
|
||||
width: 260px;
|
||||
|
||||
.progress-bar {
|
||||
.uploader-progress-bar();
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
border-right: 1px solid #f6f8f9;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
|
||||
i {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
&.image img {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-left: 90px;
|
||||
|
||||
h4 {
|
||||
padding-right: 15px;
|
||||
a {
|
||||
right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 15px 0 90px;
|
||||
}
|
||||
|
||||
&.upload-placeholder {
|
||||
height: 75px;
|
||||
background-color: transparent;
|
||||
&:after { opacity: 0; }
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.uploader-object-active();
|
||||
h4 { padding-right: 35px; }
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
h4 { padding-right: 35px; }
|
||||
|
||||
.info {
|
||||
p.size { display: none; }
|
||||
p.error { padding-bottom: 11px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-preview {
|
||||
.upload-files-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Media
|
||||
//
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.responsiv-uploader-fileupload.style-image-multi {
|
||||
.upload-object {
|
||||
width: 230px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.responsiv-uploader-fileupload.style-image-multi {
|
||||
.upload-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.upload-files-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.upload-object {
|
||||
margin-right: 0;
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// Single Image
|
||||
//
|
||||
|
||||
.responsiv-uploader-fileupload.style-image-single {
|
||||
&.is-populated {
|
||||
.upload-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-button {
|
||||
.uploader-block-button();
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.upload-object {
|
||||
padding-bottom: 66px;
|
||||
|
||||
.icon-container {
|
||||
border: 1px solid #f6f8f9;
|
||||
background: rgba(255,255,255,.5);
|
||||
|
||||
&.image img {
|
||||
border-radius: 3px;
|
||||
|
||||
// Img responsive
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
// This is needed when the image is very large and
|
||||
// being processed by dropzone on the client-side
|
||||
// the image has no height or width.
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
.uploader-progress-bar();
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 66px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
position: absolute;
|
||||
bottom: 65px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
h4 { padding-right: 20px; }
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
h4 { padding-right: 20px; }
|
||||
|
||||
.info {
|
||||
p.size { display: none; }
|
||||
p.error { padding-bottom: 11px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Media
|
||||
//
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.responsiv-uploader-fileupload.style-image-single {
|
||||
.upload-object {
|
||||
h4 { padding-right: 20px !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
@uploader-progress-bar-height: 5px;
|
||||
@uploader-progress-bar-color: #fff;
|
||||
@uploader-progress-bar-bg: #f5f5f5;
|
||||
@uploader-inactive-icon: #808b93;
|
||||
@uploader-object-active-bg: #4da7e8;
|
||||
@uploader-object-error-bg: #ab2a1c;
|
||||
@uploader-list-accent-bg: #f5f5f5;
|
||||
@uploader-list-border-color: #eeeeee;
|
||||
@uploader-inline-button-color: #333333;
|
||||
|
||||
@screen-xs-max: 767px;
|
||||
@screen-sm-max: 991px;
|
||||
@screen-md-max: 1199px;
|
||||
|
||||
@import "uploader.base.less";
|
||||
@import "uploader.imagemulti.less";
|
||||
@import "uploader.imagesingle.less";
|
||||
@import "uploader.filemulti.less";
|
||||
@import "uploader.filesingle.less";
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Backend, BackendAuth;
|
||||
|
||||
class BackendHelpers {
|
||||
|
||||
/**
|
||||
* Return a Backend URL based on a matrix of URLS and permissions
|
||||
*
|
||||
* @param array $urls Matrix of permissions and URLs
|
||||
* @param string $default Default URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getBackendURL(array $urls, string $default) :string {
|
||||
$user = BackendAuth::getUser();
|
||||
foreach ($urls as $permission => $URL) {
|
||||
if ($user->hasAccess($permission)) {
|
||||
return Backend::url($URL);
|
||||
}
|
||||
}
|
||||
return Backend::url($urls[$default]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Translator plugin is installed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isTranslatePlugin() :bool {
|
||||
return class_exists('\RainLab\Translate\Classes\Translator') && class_exists('\RainLab\Translate\Models\Message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an array|object as HTML list (UL > LI)
|
||||
*
|
||||
* @param mixed $data List items
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function array2ul($data) :string {
|
||||
$return = '';
|
||||
foreach ($data as $index => $item) {
|
||||
if (!is_string($item)) {
|
||||
$return .= '<li>' . htmlspecialchars($index, ENT_QUOTES) . '<ul>' . self::array2ul($item) . "</ul></li>";
|
||||
} else {
|
||||
$return .= '<li>';
|
||||
if (is_object($data)) {
|
||||
$return .= htmlspecialchars($index, ENT_QUOTES) . ' - ';
|
||||
}
|
||||
$return .= htmlspecialchars($item, ENT_QUOTES) .'</li>';
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymize an IPv4 address
|
||||
* (credits: https://github.com/geertw/php-ip-anonymizer)
|
||||
*
|
||||
* @param string $address IPv4 address
|
||||
*
|
||||
* @return string Anonymized address
|
||||
*/
|
||||
public static function anonymizeIPv4(string $address) :string {
|
||||
return inet_ntop(inet_pton($address) & inet_pton("255.255.255.0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract string from curly braces
|
||||
*
|
||||
* @param string $pattern Pattern to replace
|
||||
* @param string $replacement Replacement string
|
||||
* @param string $subject Strings to replace
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function replaceToken(string $pattern, string $replacement = null, string $subject) :string {
|
||||
$pattern = '/{{\s*('.$pattern.')\s*}}/';
|
||||
return preg_replace($pattern, $replacement, $subject);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Flash;
|
||||
use Request;
|
||||
use Martin\Forms\Models\Record;
|
||||
use Martin\Forms\Models\Settings;
|
||||
use Carbon\Carbon;
|
||||
use October\Rain\Exception\ApplicationException;
|
||||
use October\Rain\Exception\ValidationException;
|
||||
|
||||
class GDPR {
|
||||
|
||||
public static function cleanRecords() {
|
||||
|
||||
$gdpr_enable = Settings::get('gdpr_enable', false);
|
||||
$gdpr_days = Settings::get('gdpr_days' , false);
|
||||
|
||||
if (!$gdpr_enable) {
|
||||
Flash::error(e(trans('martin.forms::lang.classes.GDPR.alert_gdpr_disabled')));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($gdpr_enable && is_numeric($gdpr_days)) {
|
||||
$days = Carbon::now()->subDays($gdpr_days);
|
||||
$rows = Record::whereDate('created_at', '<', $days)->forceDelete();
|
||||
return $rows;
|
||||
}
|
||||
|
||||
Flash::error(e(trans('martin.forms::lang.classes.GDPR.alert_invalid_gdpr')));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Lang;
|
||||
use Config;
|
||||
use Request;
|
||||
use Session;
|
||||
use Redirect;
|
||||
use Validator;
|
||||
use AjaxException;
|
||||
use Cms\Classes\ComponentBase;
|
||||
use Martin\Forms\Models\Record;
|
||||
use Martin\Forms\Models\Settings;
|
||||
use Martin\Forms\Classes\SendMail;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Martin\Forms\Classes\BackendHelpers;
|
||||
use October\Rain\Exception\ValidationException;
|
||||
|
||||
abstract class MagicForm extends ComponentBase
|
||||
{
|
||||
|
||||
use \Martin\Forms\Classes\ReCaptcha;
|
||||
use \Martin\Forms\Classes\SharedProperties;
|
||||
|
||||
public function onRun() {
|
||||
|
||||
$this->page['recaptcha_enabled'] = $this->isReCaptchaEnabled();
|
||||
$this->page['recaptcha_misconfigured'] = $this->isReCaptchaMisconfigured();
|
||||
|
||||
if ($this->isReCaptchaEnabled()) {
|
||||
$this->loadReCaptcha();
|
||||
}
|
||||
|
||||
if ($this->isReCaptchaMisconfigured()) {
|
||||
$this->page['recaptcha_warn'] = Lang::get('martin.forms::lang.components.shared.recaptcha_warn');
|
||||
}
|
||||
|
||||
if ($this->property('inline_errors') == 'display') {
|
||||
$this->addJs('assets/js/inline-errors.js');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function settings() {
|
||||
return [
|
||||
'recaptcha_site_key' => Settings::get('recaptcha_site_key'),
|
||||
'recaptcha_secret_key' => Settings::get('recaptcha_secret_key'),
|
||||
];
|
||||
}
|
||||
|
||||
public function onFormSubmit() {
|
||||
|
||||
// FLASH PARTIAL
|
||||
$flash_partial = $this->property('messages_partial', '@flash.htm');
|
||||
|
||||
// CSRF CHECK
|
||||
if (Config::get('cms.enableCsrfProtection') && (Session::token() != post('_token'))) {
|
||||
throw new AjaxException(['#' . $this->alias . '_forms_flash' => $this->renderPartial($flash_partial, [
|
||||
'status' => 'error',
|
||||
'type' => 'danger',
|
||||
'content' => Lang::get('martin.forms::lang.components.shared.csrf_error'),
|
||||
])]);
|
||||
}
|
||||
|
||||
// LOAD TRANSLATOR PLUGIN
|
||||
if (BackendHelpers::isTranslatePlugin()) {
|
||||
$translator = \RainLab\Translate\Classes\Translator::instance();
|
||||
$translator->loadLocaleFromSession();
|
||||
$locale = $translator->getLocale();
|
||||
\RainLab\Translate\Models\Message::setContext($locale);
|
||||
}
|
||||
|
||||
// FILTER ALLOWED FIELDS
|
||||
$allow = $this->property('allowed_fields');
|
||||
if (is_array($allow) && !empty($allow)) {
|
||||
foreach ($allow as $field) {
|
||||
$post[$field] = post($field);
|
||||
}
|
||||
if ($this->isReCaptchaEnabled()) {
|
||||
$post['g-recaptcha-response'] = post('g-recaptcha-response');
|
||||
}
|
||||
} else {
|
||||
$post = post();
|
||||
}
|
||||
|
||||
// SANITIZE FORM DATA
|
||||
if ($this->property('sanitize_data') == 'htmlspecialchars') {
|
||||
$post = $this->array_map_recursive(function ($value) {
|
||||
return htmlspecialchars($value, ENT_QUOTES);
|
||||
}, $post);
|
||||
}
|
||||
|
||||
// VALIDATION PARAMETERS
|
||||
$rules = (array)$this->property('rules');
|
||||
$msgs = (array)$this->property('rules_messages');
|
||||
$custom_attributes = (array)$this->property('custom_attributes');
|
||||
|
||||
// TRANSLATE CUSTOM ERROR MESSAGES
|
||||
if (BackendHelpers::isTranslatePlugin()) {
|
||||
foreach ($msgs as $rule => $msg) {
|
||||
$msgs[$rule] = \RainLab\Translate\Models\Message::trans($msg);
|
||||
}
|
||||
}
|
||||
|
||||
// ADD reCAPTCHA VALIDATION
|
||||
if ($this->isReCaptchaEnabled() && $this->property('recaptcha_size') != 'invisible') {
|
||||
$rules['g-recaptcha-response'] = 'required';
|
||||
}
|
||||
|
||||
// DO FORM VALIDATION
|
||||
$validator = Validator::make($post, $rules, $msgs, $custom_attributes);
|
||||
|
||||
// NICE reCAPTCHA FIELD NAME
|
||||
if ($this->isReCaptchaEnabled()) {
|
||||
$fields_names = ['g-recaptcha-response' => 'reCAPTCHA'];
|
||||
$validator->setAttributeNames(array_merge($fields_names, $custom_attributes));
|
||||
}
|
||||
|
||||
// VALIDATE ALL + CAPTCHA EXISTS
|
||||
if ($validator->fails()) {
|
||||
|
||||
// GET DEFAULT ERROR MESSAGE
|
||||
$message = $this->property('messages_errors');
|
||||
|
||||
// LOOK FOR TRANSLATION
|
||||
if (BackendHelpers::isTranslatePlugin()) {
|
||||
$message = \RainLab\Translate\Models\Message::trans($message);
|
||||
}
|
||||
|
||||
// THROW ERRORS
|
||||
if ($this->property('inline_errors') == 'display') {
|
||||
throw new ValidationException($validator);
|
||||
} else {
|
||||
throw new AjaxException($this->_exceptionResponse($validator, [
|
||||
'status' => 'error',
|
||||
'type' => 'danger',
|
||||
'title' => $message,
|
||||
'list' => $validator->messages()->all(),
|
||||
'errors' => json_encode($validator->messages()->messages()),
|
||||
'jscript' => $this->property('js_on_error'),
|
||||
]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// IF FIRST VALIDATION IS OK, VALIDATE CAPTCHA vs GOOGLE
|
||||
// (this prevents to resolve captcha after every form error)
|
||||
if ($this->isReCaptchaEnabled()) {
|
||||
|
||||
// PREPARE RECAPTCHA VALIDATION
|
||||
$rules = ['g-recaptcha-response' => 'recaptcha'];
|
||||
$err_msg = ['g-recaptcha-response.recaptcha' => Lang::get('martin.forms::lang.validation.recaptcha_error')];
|
||||
|
||||
// DO SECOND VALIDATION
|
||||
$validator = Validator::make($post, $rules, $err_msg);
|
||||
|
||||
// VALIDATE ALL + CAPTCHA EXISTS
|
||||
if ($validator->fails()) {
|
||||
|
||||
// THROW ERRORS
|
||||
if ($this->property('inline_errors') == 'display') {
|
||||
throw new ValidationException($validator);
|
||||
} else {
|
||||
throw new AjaxException($this->_exceptionResponse($validator, [
|
||||
'status' => 'error',
|
||||
'type' => 'danger',
|
||||
'content' => Lang::get('martin.forms::lang.validation.recaptcha_error'),
|
||||
'errors' => json_encode($validator->messages()->messages()),
|
||||
'jscript' => $this->property('js_on_error'),
|
||||
]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// REMOVE EXTRA FIELDS FROM STORED DATA
|
||||
unset($post['_token'], $post['g-recaptcha-response'], $post['_session_key'], $post['_uploader']);
|
||||
|
||||
// FIRE BEFORE SAVE EVENT
|
||||
Event::fire('martin.forms.beforeSaveRecord', [&$post, $this]);
|
||||
|
||||
if (count($custom_attributes)) {
|
||||
$post = collect($post)->mapWithKeys(function ($val, $key) use ($custom_attributes) {
|
||||
return [array_get($custom_attributes, $key, $key) => $val];
|
||||
})->all();
|
||||
}
|
||||
|
||||
$record = new Record;
|
||||
$record->ip = $this->getIP();
|
||||
$record->created_at = date('Y-m-d H:i:s');
|
||||
|
||||
// SAVE RECORD TO DATABASE
|
||||
if (! $this->property('skip_database')) {
|
||||
$record->form_data = json_encode($post, JSON_UNESCAPED_UNICODE);
|
||||
if ($this->property('group') != '') {
|
||||
$record->group = $this->property('group');
|
||||
}
|
||||
$record->save(null, post('_session_key'));
|
||||
}
|
||||
|
||||
// SEND NOTIFICATION EMAIL
|
||||
if ($this->property('mail_enabled')) {
|
||||
SendMail::sendNotification($this->getProperties(), $post, $record, $record->files);
|
||||
}
|
||||
|
||||
// SEND AUTORESPONSE EMAIL
|
||||
if ($this->property('mail_resp_enabled')) {
|
||||
SendMail::sendAutoResponse($this->getProperties(), $post, $record);
|
||||
}
|
||||
|
||||
// FIRE AFTER SAVE EVENT
|
||||
Event::fire('martin.forms.afterSaveRecord', [&$post, $this, $record]);
|
||||
|
||||
// CHECK FOR REDIRECT
|
||||
if ($this->property('redirect')) {
|
||||
return Redirect::to($this->property('redirect'));
|
||||
}
|
||||
|
||||
// GET DEFAULT SUCCESS MESSAGE
|
||||
$message = $this->property('messages_success');
|
||||
|
||||
// LOOK FOR TRANSLATION
|
||||
if (BackendHelpers::isTranslatePlugin()) {
|
||||
$message = \RainLab\Translate\Models\Message::trans($message);
|
||||
}
|
||||
|
||||
// DISPLAY SUCCESS MESSAGE
|
||||
return ['#' . $this->alias . '_forms_flash' => $this->renderPartial($flash_partial, [
|
||||
'status' => 'success',
|
||||
'type' => 'success',
|
||||
'content' => $message,
|
||||
'jscript' => $this->prepareJavaScript(),
|
||||
])];
|
||||
|
||||
}
|
||||
|
||||
private function _exceptionResponse($validator, $params) {
|
||||
|
||||
// FLASH PARTIAL
|
||||
$flash_partial = $this->property('messages_partial', '@flash.htm');
|
||||
|
||||
// EXCEPTION RESPONSE
|
||||
$response = ['#' . $this->alias . '_forms_flash' => $this->renderPartial($flash_partial, $params)];
|
||||
|
||||
// INCLUDE ERROR FIELDS IF REQUIRED
|
||||
if ($this->property('inline_errors') != 'disabled') {
|
||||
$response['error_fields'] = $validator->messages();
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
private function prepareJavaScript()
|
||||
{
|
||||
$code = false;
|
||||
|
||||
/* SUCCESS JS */
|
||||
if ($this->property('js_on_success') != '') {
|
||||
$code .= $this->property('js_on_success');
|
||||
}
|
||||
|
||||
/* RECAPTCHA JS */
|
||||
if ($this->isReCaptchaEnabled()) {
|
||||
$code .= $this->renderPartial('@js/recaptcha.htm');
|
||||
}
|
||||
|
||||
/* RESET FORM JS */
|
||||
if ($this->property('reset_form')) {
|
||||
$params = ['id' => '#' . $this->alias . '_forms_flash'];
|
||||
$code .= $this->renderPartial('@js/reset-form.htm', $params);
|
||||
}
|
||||
|
||||
/* RESET UPLOAD FORM */
|
||||
if ($this->property('reset_form') && $this->property('uploader_enable')) {
|
||||
$params = ['id' => $this->alias];
|
||||
$code .= $this->renderPartial('@js/reset-uploader.htm', $params);
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
private function getIP()
|
||||
{
|
||||
if ($this->property('anonymize_ip') == 'full') {
|
||||
return '(Not stored)';
|
||||
}
|
||||
|
||||
$ip = Request::getClientIp();
|
||||
|
||||
if ($this->property('anonymize_ip') == 'partial') {
|
||||
return BackendHelpers::anonymizeIPv4($ip);
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
private function array_map_recursive($callback, $array)
|
||||
{
|
||||
$func = function ($item) use (&$func, &$callback) {
|
||||
return is_array($item) ? array_map($func, $item) : call_user_func($callback, $item);
|
||||
};
|
||||
|
||||
return array_map($func, $array);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Session;
|
||||
use Martin\Forms\Classes\BackendHelpers;
|
||||
use Martin\Forms\Models\Settings;
|
||||
use RainLab\Translate\Classes\Translator;
|
||||
|
||||
trait ReCaptcha {
|
||||
|
||||
/**
|
||||
* @var RainLab\Translate\Classes\Translator Translator object.
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @var string The active locale code.
|
||||
*/
|
||||
public $activeLocale;
|
||||
|
||||
public function init() {
|
||||
if (BackendHelpers::isTranslatePlugin()) {
|
||||
$this->translator = Translator::instance();
|
||||
}
|
||||
}
|
||||
|
||||
private function isReCaptchaEnabled() {
|
||||
return ($this->property('recaptcha_enabled') && Settings::get('recaptcha_site_key') != '' && Settings::get('recaptcha_secret_key') != '');
|
||||
}
|
||||
|
||||
private function isReCaptchaMisconfigured() {
|
||||
return ($this->property('recaptcha_enabled') && (Settings::get('recaptcha_site_key') == '' || Settings::get('recaptcha_secret_key') == ''));
|
||||
}
|
||||
|
||||
private function getReCaptchaLang($lang='') {
|
||||
if (BackendHelpers::isTranslatePlugin()) {
|
||||
$lang = '&hl=' . $this->activeLocale = $this->translator->getLocale();
|
||||
} else {
|
||||
$lang = '&hl=' . $this->activeLocale = app()->getLocale();
|
||||
}
|
||||
return $lang;
|
||||
}
|
||||
|
||||
private function loadReCaptcha() {
|
||||
$this->addJs('https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit'.$this->getReCaptchaLang(), ['async', 'defer']);
|
||||
$this->addJs('assets/js/recaptcha.js');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Request;
|
||||
use Martin\Forms\Models\Settings;
|
||||
|
||||
class ReCaptchaValidator {
|
||||
|
||||
public function validateReCaptcha($attribute, $value, $parameters) {
|
||||
$secret_key = Settings::get('recaptcha_secret_key');
|
||||
$recaptcha = post('g-recaptcha-response');
|
||||
$ip = Request::getClientIp();
|
||||
$URL = "https://www.google.com/recaptcha/api/siteverify?secret=$secret_key&response=$recaptcha&remoteip=$ip";
|
||||
$response = json_decode(file_get_contents($URL), true);
|
||||
return ($response['success'] == true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Mail;
|
||||
use System\Models\MailTemplate;
|
||||
use Martin\Forms\Classes\BackendHelpers;
|
||||
|
||||
class SendMail {
|
||||
|
||||
public static function sendNotification($properties, $post, $record, $files) {
|
||||
|
||||
// CHECK IF THERE IS AT LEAST ONE MAIL ADDRESS
|
||||
if (!isset($properties['mail_recipients'])) {
|
||||
$properties['mail_recipients'] = false;
|
||||
}
|
||||
|
||||
// CHECK IF THERE IS AT LEAST ONE MAIL ADDRESS
|
||||
if (!isset($properties['mail_bcc'])) {
|
||||
$properties['mail_bcc'] = false;
|
||||
}
|
||||
|
||||
if (is_array($properties['mail_recipients']) || is_array($properties['mail_bcc'])) {
|
||||
|
||||
// CUSTOM TEMPLATE
|
||||
$template = isset($properties['mail_template']) && $properties['mail_template'] != '' && MailTemplate::findOrMakeTemplate($properties['mail_template']) ? $properties['mail_template'] : 'martin.forms::mail.notification';
|
||||
|
||||
$data = [
|
||||
'id' => $record->id,
|
||||
'data' => $post,
|
||||
'ip' => $record->ip,
|
||||
'date' => $record->created_at
|
||||
];
|
||||
|
||||
// CHECK FOR CUSTOM SUBJECT
|
||||
if (isset($properties['mail_subject'])) {
|
||||
|
||||
// set date format
|
||||
$dateFormat = $properties['emails_date_format'] ?? 'Y-m-d';
|
||||
|
||||
// REPLACE RECORD TOKENS IN SUBJECT
|
||||
$properties['mail_subject'] = BackendHelpers::replaceToken('record.id', $data['id'], $properties['mail_subject']);
|
||||
$properties['mail_subject'] = BackendHelpers::replaceToken('record.ip', $data['ip'], $properties['mail_subject']);
|
||||
$properties['mail_subject'] = BackendHelpers::replaceToken('record.date', date($dateFormat), $properties['mail_subject']);
|
||||
|
||||
// REPLACE FORM FIELDS TOKENS IN SUBJECT
|
||||
foreach ($data['data'] as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
$properties['mail_subject'] = BackendHelpers::replaceToken('form.'.$key, $value, $properties['mail_subject']);
|
||||
}
|
||||
}
|
||||
|
||||
// SET CUSTOM SUBJECT
|
||||
$data['subject'] = $properties['mail_subject'];
|
||||
|
||||
}
|
||||
|
||||
// SEND NOTIFICATION EMAIL
|
||||
Mail::sendTo($properties['mail_recipients'], $template, $data, function ($message) use ($properties, $post, $files) {
|
||||
|
||||
// SEND BLIND CARBON COPY
|
||||
if (isset($properties['mail_bcc']) && is_array($properties['mail_bcc'])) {
|
||||
$message->bcc($properties['mail_bcc']);
|
||||
}
|
||||
|
||||
// USE CUSTOM SUBJECT
|
||||
if (isset($properties['mail_subject'])) {
|
||||
$message->subject($properties['mail_subject']);
|
||||
}
|
||||
|
||||
// ADD REPLY TO ADDRESS
|
||||
if (isset($properties['mail_replyto']) && isset($post[$properties['mail_replyto']])) {
|
||||
$message->replyTo($post[$properties['mail_replyto']]);
|
||||
}
|
||||
|
||||
// ADD UPLOADS
|
||||
if (isset($properties['mail_uploads']) && $properties['mail_uploads'] && !empty($files)) {
|
||||
foreach ($files as $file) {
|
||||
$message->attach($file->getLocalPath(), ['as' => $file->getFilename()]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function sendAutoResponse($properties, $post, $record) {
|
||||
|
||||
$data = [
|
||||
'id' => $record->id,
|
||||
'data' => $post,
|
||||
'ip' => $record->ip,
|
||||
'date' => $record->created_at
|
||||
];
|
||||
|
||||
// CHECK FOR CUSTOM SUBJECT
|
||||
if (isset($properties['mail_resp_subject'])) {
|
||||
|
||||
// set date format
|
||||
$dateFormat = $properties['emails_date_format'] ?? 'Y-m-d';
|
||||
|
||||
// REPLACE RECORD TOKENS IN SUBJECT
|
||||
$properties['mail_resp_subject'] = BackendHelpers::replaceToken('record.id', $data['id'], $properties['mail_resp_subject']);
|
||||
$properties['mail_resp_subject'] = BackendHelpers::replaceToken('record.ip', $data['ip'], $properties['mail_resp_subject']);
|
||||
$properties['mail_resp_subject'] = BackendHelpers::replaceToken('record.date', date($dateFormat), $properties['mail_resp_subject']);
|
||||
|
||||
// REPLACE FORM FIELDS TOKENS IN SUBJECT
|
||||
foreach ($data['data'] as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
$properties['mail_resp_subject'] = BackendHelpers::replaceToken('form.'.$key, $value, $properties['mail_resp_subject']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = isset($properties['mail_resp_field']) ? $properties['mail_resp_field'] : null;
|
||||
$to = isset($post[$response]) ? $post[$response] : null;
|
||||
$from = isset($properties['mail_resp_from']) ? $properties['mail_resp_from'] : null;
|
||||
$subject = isset($properties['mail_resp_subject']) ? $properties['mail_resp_subject'] : null;
|
||||
|
||||
if (filter_var($to, FILTER_VALIDATE_EMAIL) && filter_var($from, FILTER_VALIDATE_EMAIL)) {
|
||||
|
||||
// CUSTOM TEMPLATE
|
||||
$template = isset($properties['mail_resp_template']) && $properties['mail_resp_template'] != '' && MailTemplate::findOrMakeTemplate($properties['mail_resp_template']) ? $properties['mail_resp_template'] : 'martin.forms::mail.autoresponse';
|
||||
|
||||
Mail::sendTo($to, $template, [
|
||||
'id' => $record->id,
|
||||
'data' => $post,
|
||||
'ip' => $record->ip,
|
||||
'date' => $record->created_at
|
||||
], function ($message) use ($from, $subject) {
|
||||
$message->from($from);
|
||||
if (isset($subject)) {
|
||||
$message->subject($subject);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Lang;
|
||||
|
||||
trait SharedProperties {
|
||||
|
||||
public function defineProperties() {
|
||||
return [
|
||||
'group' => [
|
||||
'title' => 'martin.forms::lang.components.shared.group.title',
|
||||
'description' => 'martin.forms::lang.components.shared.group.description',
|
||||
'type' => 'string',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'rules' => [
|
||||
'title' => 'martin.forms::lang.components.shared.rules.title',
|
||||
'description' => 'martin.forms::lang.components.shared.rules.description',
|
||||
'type' => 'dictionary',
|
||||
'group' => 'martin.forms::lang.components.shared.group_validation',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'rules_messages' => [
|
||||
'title' => 'martin.forms::lang.components.shared.rules_messages.title',
|
||||
'description' => 'martin.forms::lang.components.shared.rules_messages.description',
|
||||
'type' => 'dictionary',
|
||||
'group' => 'martin.forms::lang.components.shared.group_validation',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'custom_attributes' => [
|
||||
'title' => 'martin.forms::lang.components.shared.custom_attributes.title',
|
||||
'description' => 'martin.forms::lang.components.shared.custom_attributes.description',
|
||||
'type' => 'dictionary',
|
||||
'group' => 'martin.forms::lang.components.shared.group_validation',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'messages_success' => [
|
||||
'title' => 'martin.forms::lang.components.shared.messages_success.title',
|
||||
'description' => 'martin.forms::lang.components.shared.messages_success.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_messages',
|
||||
'default' => Lang::get('martin.forms::lang.components.shared.messages_success.default'),
|
||||
'showExternalParam' => false,
|
||||
'validation' => ['required' => ['message' => Lang::get('martin.forms::lang.components.shared.validation_req')]]
|
||||
],
|
||||
'messages_errors' => [
|
||||
'title' => 'martin.forms::lang.components.shared.messages_errors.title',
|
||||
'description' => 'martin.forms::lang.components.shared.messages_errors.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_messages',
|
||||
'default' => Lang::get('martin.forms::lang.components.shared.messages_errors.default'),
|
||||
'showExternalParam' => false,
|
||||
'validation' => ['required' => ['message' => Lang::get('martin.forms::lang.components.shared.validation_req')]]
|
||||
],
|
||||
'messages_partial' => [
|
||||
'title' => 'martin.forms::lang.components.shared.messages_partial.title',
|
||||
'description' => 'martin.forms::lang.components.shared.messages_partial.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_messages',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_enabled' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_enabled.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_enabled.description',
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_subject' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_subject.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_subject.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_recipients' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_recipients.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_recipients.description',
|
||||
'type' => 'stringList',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_bcc' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_bcc.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_bcc.description',
|
||||
'type' => 'stringList',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_replyto' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_replyto.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_replyto.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_template' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_template.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_template.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_resp_enabled' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_resp_enabled.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_resp_enabled.description',
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail_resp',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_resp_field' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_resp_field.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_resp_field.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail_resp',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_resp_from' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_resp_from.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_resp_from.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail_resp',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_resp_subject' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_resp_subject.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_resp_subject.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail_resp',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'mail_resp_template' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_template.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_template.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail_resp',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'reset_form' => [
|
||||
'title' => 'martin.forms::lang.components.shared.reset_form.title',
|
||||
'description' => 'martin.forms::lang.components.shared.reset_form.description',
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_settings',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'redirect' => [
|
||||
'title' => 'martin.forms::lang.components.shared.redirect.title',
|
||||
'description' => 'martin.forms::lang.components.shared.redirect.description',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_settings',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'inline_errors' => [
|
||||
'title' => 'martin.forms::lang.components.shared.inline_errors.title',
|
||||
'description' => 'martin.forms::lang.components.shared.inline_errors.description',
|
||||
'type' => 'dropdown',
|
||||
'options' => ['disabled' => 'martin.forms::lang.components.shared.inline_errors.disabled', 'display' => 'martin.forms::lang.components.shared.inline_errors.display', 'variable' => 'martin.forms::lang.components.shared.inline_errors.variable'],
|
||||
'default' => 'disabled',
|
||||
'group' => 'martin.forms::lang.components.shared.group_settings',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'js_on_success' => [
|
||||
'title' => 'martin.forms::lang.components.shared.js_on_success.title',
|
||||
'description' => 'martin.forms::lang.components.shared.js_on_success.description',
|
||||
'type' => 'text',
|
||||
'group' => 'martin.forms::lang.components.shared.group_settings',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'js_on_error' => [
|
||||
'title' => 'martin.forms::lang.components.shared.js_on_error.title',
|
||||
'description' => 'martin.forms::lang.components.shared.js_on_error.description',
|
||||
'type' => 'text',
|
||||
'group' => 'martin.forms::lang.components.shared.group_settings',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'allowed_fields' => [
|
||||
'title' => 'martin.forms::lang.components.shared.allowed_fields.title',
|
||||
'description' => 'martin.forms::lang.components.shared.allowed_fields.description',
|
||||
'type' => 'stringList',
|
||||
'group' => 'martin.forms::lang.components.shared.group_security',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'sanitize_data' => [
|
||||
'title' => 'martin.forms::lang.components.shared.sanitize_data.title',
|
||||
'description' => 'martin.forms::lang.components.shared.sanitize_data.description',
|
||||
'type' => 'dropdown',
|
||||
'options' => ['disabled' => 'martin.forms::lang.components.shared.sanitize_data.disabled', 'htmlspecialchars' => 'martin.forms::lang.components.shared.sanitize_data.htmlspecialchars'],
|
||||
'default' => 'disabled',
|
||||
'group' => 'martin.forms::lang.components.shared.group_security',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'anonymize_ip' => [
|
||||
'title' => 'martin.forms::lang.components.shared.anonymize_ip.title',
|
||||
'description' => 'martin.forms::lang.components.shared.anonymize_ip.description',
|
||||
'type' => 'dropdown',
|
||||
'options' => ['disabled' => 'martin.forms::lang.components.shared.anonymize_ip.disabled', 'partial' => 'martin.forms::lang.components.shared.anonymize_ip.partial', 'full' => 'martin.forms::lang.components.shared.anonymize_ip.full'],
|
||||
'default' => 'disabled',
|
||||
'group' => 'martin.forms::lang.components.shared.group_security',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'recaptcha_enabled' => [
|
||||
'title' => 'martin.forms::lang.components.shared.recaptcha_enabled.title',
|
||||
'description' => 'martin.forms::lang.components.shared.recaptcha_enabled.description',
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_recaptcha',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'recaptcha_theme' => [
|
||||
'title' => 'martin.forms::lang.components.shared.recaptcha_theme.title',
|
||||
'description' => 'martin.forms::lang.components.shared.recaptcha_theme.description',
|
||||
'type' => 'dropdown',
|
||||
'options' => ['light' => 'martin.forms::lang.components.shared.recaptcha_theme.light', 'dark' => 'martin.forms::lang.components.shared.recaptcha_theme.dark'],
|
||||
'default' => 'light',
|
||||
'group' => 'martin.forms::lang.components.shared.group_recaptcha',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'recaptcha_type' => [
|
||||
'title' => 'martin.forms::lang.components.shared.recaptcha_type.title',
|
||||
'description' => 'martin.forms::lang.components.shared.recaptcha_type.description',
|
||||
'type' => 'dropdown',
|
||||
'options' => ['image' => 'martin.forms::lang.components.shared.recaptcha_type.image', 'audio' => 'martin.forms::lang.components.shared.recaptcha_type.audio'],
|
||||
'default' => 'image',
|
||||
'group' => 'martin.forms::lang.components.shared.group_recaptcha',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'recaptcha_size' => [
|
||||
'title' => 'martin.forms::lang.components.shared.recaptcha_size.title',
|
||||
'description' => 'martin.forms::lang.components.shared.recaptcha_size.description',
|
||||
'type' => 'dropdown',
|
||||
'options' => [
|
||||
'normal' => 'martin.forms::lang.components.shared.recaptcha_size.normal',
|
||||
'compact' => 'martin.forms::lang.components.shared.recaptcha_size.compact',
|
||||
'invisible' => 'martin.forms::lang.components.shared.recaptcha_size.invisible',
|
||||
],
|
||||
'default' => 'normal',
|
||||
'group' => 'martin.forms::lang.components.shared.group_recaptcha',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'skip_database' => [
|
||||
'title' => 'martin.forms::lang.components.shared.skip_database.title',
|
||||
'description' => 'martin.forms::lang.components.shared.skip_database.description',
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_advanced',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'emails_date_format' => [
|
||||
'title' => 'martin.forms::lang.components.shared.emails_date_format.title',
|
||||
'description' => 'martin.forms::lang.components.shared.emails_date_format.description',
|
||||
'default' => 'Y-m-d',
|
||||
'group' => 'martin.forms::lang.components.shared.group_advanced',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Classes;
|
||||
|
||||
use Martin\Forms\Models\Record;
|
||||
|
||||
class UnreadRecords {
|
||||
|
||||
public static function getTotal() {
|
||||
$unread = Record::where('unread', 1)->count();
|
||||
return ($unread > 0) ? $unread : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Components;
|
||||
|
||||
use Martin\Forms\Classes\MagicForm;
|
||||
|
||||
class EmptyForm extends MagicForm {
|
||||
|
||||
public function componentDetails() {
|
||||
return [
|
||||
'name' => 'martin.forms::lang.components.empty_form.name',
|
||||
'description' => 'martin.forms::lang.components.empty_form.description',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Components;
|
||||
|
||||
use Martin\Forms\Classes\MagicForm;
|
||||
|
||||
class GenericForm extends MagicForm {
|
||||
|
||||
public function componentDetails() {
|
||||
return [
|
||||
'name' => 'martin.forms::lang.components.generic_form.name',
|
||||
'description' => 'martin.forms::lang.components.generic_form.description',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Components;
|
||||
|
||||
use Lang;
|
||||
use October\Rain\Filesystem\Definitions;
|
||||
use Martin\Forms\Classes\MagicForm;
|
||||
use Martin\Forms\Models\Record;
|
||||
|
||||
class UploadForm extends MagicForm {
|
||||
|
||||
use \Martin\Forms\Traits\FileUploader;
|
||||
|
||||
public function componentDetails() {
|
||||
return [
|
||||
'name' => 'martin.forms::lang.components.upload_form.name',
|
||||
'description' => 'martin.forms::lang.components.upload_form.description',
|
||||
];
|
||||
}
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
$this->fileTypes = $this->processFileTypes(true);
|
||||
$this->maxSize = $this->property('maxSize');
|
||||
$this->placeholderText = $this->property('placeholderText');
|
||||
$this->removeText = $this->property('removeText');
|
||||
$this->setProperty('deferredBinding', 1);
|
||||
$this->bindModel('files', new Record);
|
||||
}
|
||||
|
||||
public function onRun() {
|
||||
parent::onRun();
|
||||
$this->addCss('assets/css/uploader.css');
|
||||
$this->addJs('assets/vendor/dropzone/dropzone.js');
|
||||
$this->addJs('assets/js/uploader.js');
|
||||
$this->isMulti = $this->property('uploader_multi');
|
||||
if($result = $this->checkUploadAction()) { return $result; }
|
||||
}
|
||||
|
||||
public function defineProperties() {
|
||||
$local = [
|
||||
'mail_uploads' => [
|
||||
'title' => 'martin.forms::lang.components.shared.mail_uploads.title',
|
||||
'description' => 'martin.forms::lang.components.shared.mail_uploads.description',
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'group' => 'martin.forms::lang.components.shared.group_mail',
|
||||
'showExternalParam' => false
|
||||
],
|
||||
'uploader_enable' => [
|
||||
'title' => 'martin.forms::lang.components.shared.uploader_enable.title',
|
||||
'description' => 'martin.forms::lang.components.shared.uploader_enable.description',
|
||||
'default' => false,
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_uploader',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'uploader_multi' => [
|
||||
'title' => 'martin.forms::lang.components.shared.uploader_multi.title',
|
||||
'description' => 'martin.forms::lang.components.shared.uploader_multi.description',
|
||||
'default' => true,
|
||||
'type' => 'checkbox',
|
||||
'group' => 'martin.forms::lang.components.shared.group_uploader',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'placeholderText' => [
|
||||
'title' => 'martin.forms::lang.components.shared.uploader_pholder.title',
|
||||
'description' => 'martin.forms::lang.components.shared.uploader_pholder.description',
|
||||
'default' => Lang::get('martin.forms::lang.components.shared.uploader_pholder.default'),
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_uploader',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'removeText' => [
|
||||
'title' => 'martin.forms::lang.components.shared.uploader_remFile.title',
|
||||
'description' => 'martin.forms::lang.components.shared.uploader_remFile.description',
|
||||
'default' => Lang::get('martin.forms::lang.components.shared.uploader_remFile.default'),
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_uploader',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'maxSize' => [
|
||||
'title' => 'martin.forms::lang.components.shared.uploader_maxsize.title',
|
||||
'description' => 'martin.forms::lang.components.shared.uploader_maxsize.description',
|
||||
'default' => '5',
|
||||
'type' => 'string',
|
||||
'group' => 'martin.forms::lang.components.shared.group_uploader',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
'fileTypes' => [
|
||||
'title' => 'martin.forms::lang.components.shared.uploader_types.title',
|
||||
'description' => 'martin.forms::lang.components.shared.uploader_types.description',
|
||||
'default' => Definitions::get('defaultExtensions'),
|
||||
'type' => 'stringList',
|
||||
'group' => 'martin.forms::lang.components.shared.group_uploader',
|
||||
'showExternalParam' => false,
|
||||
],
|
||||
];
|
||||
return array_merge(parent::defineProperties(), $local);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<h3>Here goes your custom form</h3>
|
||||
<p>Override HTML by creating a new partial called <strong>default.htm</strong> (more info <a href="https://octobercms.com/docs/cms/components#overriding-partials" target="_blank">here</a>)</p>
|
||||
<p>You can copy/paste this basic template:</p>
|
||||
<pre>
|
||||
<form data-request="{{ __SELF__ }}::onFormSubmit">
|
||||
{{ form_token() }}
|
||||
<div id="{{ __SELF__ }}_forms_flash"></div>
|
||||
<!-- YOUR FORM FIELDS -->
|
||||
{% partial '@recaptcha' %}
|
||||
<!-- SUBMIT BUTTON -->
|
||||
</form>
|
||||
</pre>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<form data-request="{{ __SELF__ }}::onFormSubmit">
|
||||
|
||||
{{ form_token() }}
|
||||
|
||||
<div id="{{ __SELF__ }}_forms_flash"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email:</label>
|
||||
<input type="text" id="email" name="email" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="subject">Subject:</label>
|
||||
<input type="text" id="subject" name="subject" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea id="comments" name="comments" rows="8" cols="80"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{% partial '@recaptcha' %}
|
||||
</div>
|
||||
|
||||
<button id="simpleContactSubmitButton" type="submit" class="btn btn-default">Submit</button>
|
||||
|
||||
</form>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{% spaceless %}
|
||||
|
||||
<div class="alert alert-{{ type }} alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
|
||||
{% if title %}
|
||||
<h4>{{ title }}</h4>
|
||||
{% endif %}
|
||||
|
||||
{% if content %}
|
||||
<p>{{ content }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list %}
|
||||
<ul>
|
||||
{% for item in list %}
|
||||
<li>{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% if jscript %}
|
||||
<script>
|
||||
{% if errors %}
|
||||
var errors = JSON.parse('{{ errors|raw }}');
|
||||
{% endif %}
|
||||
{{ jscript|raw }}
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% endspaceless %}
|
||||
|
|
@ -0,0 +1 @@
|
|||
resetReCaptcha('{{ __SELF__ }}');
|
||||
|
|
@ -0,0 +1 @@
|
|||
$('{{ id }}').parents('form')[0].reset();
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
var dz = uploadDropZones['{{ id }}'];
|
||||
if(dz) { dz.uploader.dropzone.removeAllFiles(); dz.uploader.evalIsPopulated(); }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{% if (recaptcha_enabled) %}
|
||||
<div id="{{ __SELF__ }}" class="g-recaptcha" data-sitekey="{{ __SELF__.settings.recaptcha_site_key }}" data-theme="{{ __SELF__.property('recaptcha_theme') }}" data-type="{{ __SELF__.property('recaptcha_type') }}" data-size="{{ __SELF__.property('recaptcha_size') }}"></div>
|
||||
{% elseif (recaptcha_misconfigured) %}
|
||||
<h5>{{ recaptcha_warn }}</h5>
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
{{ form_ajax(__SELF__ ~ '::onFormSubmit') }}
|
||||
|
||||
<div id="{{ __SELF__ }}_forms_flash"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<h2>Apply for online job</h2>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email:</label>
|
||||
<input type="text" id="email" name="email" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="position">Position:</label>
|
||||
<select class="form-control" id="position" name="position">
|
||||
<option value=""></option>
|
||||
<option value="Lead">Lead</option>
|
||||
<option value="Senior">Senior</option>
|
||||
<option value="Direct">Direct</option>
|
||||
<option value="Corporate">Corporate</option>
|
||||
<option value="Dynamic">Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="skills">Skills:</label>
|
||||
<textarea id="skills" name="skills" class="form-control" rows="8" cols="80" placeholder="Summary of skills"></textarea>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
<h4>Upload your resume</h4>
|
||||
{% partial '@file-upload' %}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
{% partial '@recaptcha' %}
|
||||
</div>
|
||||
|
||||
{{ form_submit() }}
|
||||
|
||||
{{ form_close() }}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<div
|
||||
class="responsiv-uploader-fileupload style-file-multi is-multi {{ __SELF__.isPopulated() ? 'is-populated' }}"
|
||||
data-control="fileupload"
|
||||
data-template="#uploaderTemplate{{ __SELF__ }}"
|
||||
data-unique-id="{{ __SELF__ }}"
|
||||
{% if __SELF__.fileTypes %}data-file-types="{{ __SELF__.fileTypes }}"{% endif %}
|
||||
>
|
||||
|
||||
<!-- Field placeholder -->
|
||||
<input type="hidden" name="_uploader[{{ __SELF__.attribute }}]" value="" />
|
||||
|
||||
<!-- Upload Button -->
|
||||
<button type="button" class="ui button btn btn-default oc-icon-upload upload-button">
|
||||
{{ __SELF__.placeholderText }}
|
||||
</button>
|
||||
|
||||
<!-- Existing files -->
|
||||
<div class="upload-files-container">
|
||||
{% for file in __SELF__.fileList %}
|
||||
<div class="upload-object is-success" data-id="{{ file.id }}" data-path="{{ file.pathUrl }}">
|
||||
<div class="icon-container">
|
||||
{% if file.isImage %}
|
||||
<img src="{{ file.thumbUrl }}" alt="" />
|
||||
{% else %}
|
||||
<img src="{{ 'plugins/martin/forms/assets/imgs/upload.png'|app }}" alt="" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="info">
|
||||
<h4 class="filename">
|
||||
<span data-dz-name>{{ file.title ?: file.file_name }}</span>
|
||||
</h4>
|
||||
<p class="size">{{ file.sizeToString }}</p>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="upload-remove-button"
|
||||
data-request="{{ __SELF__ ~ '::onRemoveAttachment' }}"
|
||||
data-request-confirm="{{ __SELF__.removeText }}"
|
||||
data-request-data="file_id: {{ file.id }}"
|
||||
>×</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Template for new files -->
|
||||
<script type="text/template" id="uploaderTemplate{{ __SELF__ }}">
|
||||
<div class="upload-object dz-preview dz-file-preview">
|
||||
<div class="icon-container">
|
||||
<img data-dz-thumbnail src="{{ 'plugins/martin/forms/assets/imgs/upload.png'|app }}" />
|
||||
</div>
|
||||
<div class="info">
|
||||
<h4 class="filename">
|
||||
<span data-dz-name></span>
|
||||
</h4>
|
||||
<p class="size" data-dz-size></p>
|
||||
<p class="error"><span data-dz-errormessage></span></p>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="upload-remove-button"
|
||||
data-request="{{ __SELF__ ~ '::onRemoveAttachment' }}"
|
||||
data-request-confirm="{{ __SELF__.removeText }}"
|
||||
>×</a>
|
||||
<div class="progress-bar"><span class="upload-progress" data-dz-uploadprogress></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
{% set file = __SELF__.singleFile %}
|
||||
|
||||
<div
|
||||
class="responsiv-uploader-fileupload style-file-single {{ __SELF__.isPopulated() ? 'is-populated' }}"
|
||||
data-control="fileupload"
|
||||
data-template="#uploaderTemplate{{ __SELF__ }}"
|
||||
data-unique-id="{{ __SELF__ }}"
|
||||
{% if __SELF__.fileTypes %}data-file-types="{{ __SELF__.fileTypes }}"{% endif %}
|
||||
>
|
||||
|
||||
<!-- Field placeholder -->
|
||||
<input type="hidden" name="_uploader[{{ __SELF__.attribute }}]" value="" />
|
||||
|
||||
<!-- Upload button -->
|
||||
<button type="button" class="ui button btn btn-default upload-button">
|
||||
Browse
|
||||
</button>
|
||||
|
||||
<!-- Existing file -->
|
||||
<div class="upload-files-container">
|
||||
{% if file %}
|
||||
<div class="upload-object is-success" data-id="{{ file.id }}" data-path="{{ file.pathUrl }}">
|
||||
<div class="icon-container">
|
||||
{% if file.isImage %}
|
||||
<img src="{{ file.thumbUrl }}" alt="" />
|
||||
{% else %}
|
||||
<img src="{{ 'plugins/martin/forms/assets/imgs/upload.png'|app }}" alt="" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="info">
|
||||
<h4 class="filename">
|
||||
<span data-dz-name>{{ file.title ?: file.file_name }}</span>
|
||||
</h4>
|
||||
<p class="size">{{ file.sizeToString }}</p>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="upload-remove-button"
|
||||
data-request="{{ __SELF__ ~ '::onRemoveAttachment' }}"
|
||||
data-request-confirm="Are you sure?"
|
||||
data-request-data="file_id: {{ file.id }}"
|
||||
>×</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Empty message -->
|
||||
<div class="upload-empty-message">
|
||||
<span class="text-muted">{{ __SELF__.placeholderText }}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Template for new file -->
|
||||
<script type="text/template" id="uploaderTemplate{{ __SELF__ }}">
|
||||
<div class="upload-object dz-preview dz-file-preview">
|
||||
<div class="icon-container">
|
||||
<img data-dz-thumbnail />
|
||||
</div>
|
||||
<div class="info">
|
||||
<h4 class="filename">
|
||||
<span data-dz-name></span>
|
||||
</h4>
|
||||
<p class="size" data-dz-size></p>
|
||||
<p class="error"><span data-dz-errormessage></span></p>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="upload-remove-button"
|
||||
data-request="{{ __SELF__ ~ '::onRemoveAttachment' }}"
|
||||
data-request-confirm="Are you sure?"
|
||||
>×</a>
|
||||
<div class="progress-bar"><span class="upload-progress" data-dz-uploadprogress></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{% if __SELF__.property('uploader_enable') == 0 %}
|
||||
|
||||
<div style="color:#a94442; background-color:#f2dede; border:solid 1px #ebccd1; border-radius:4px;">
|
||||
<h5><strong>Warning</strong></h5>
|
||||
<h6>Uploads are disabled.</h6>
|
||||
<h6>You need to explicitly enable this option on the component (this is a security measure).</h6>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
{% if __SELF__.isMulti %}
|
||||
|
||||
{% partial __SELF__ ~ '::file-multi' %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{% partial __SELF__ ~ '::file-single' %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "martin/forms-plugin",
|
||||
"type": "october-plugin",
|
||||
"description": "None",
|
||||
"require": {
|
||||
"composer/installers": "~1.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Controllers;
|
||||
|
||||
use BackendMenu, Response;
|
||||
use Backend\Classes\Controller;
|
||||
use League\Csv\AbstractCsv;
|
||||
use League\Csv\Writer as CsvWriter;
|
||||
use SplTempFileObject;
|
||||
use Martin\Forms\Models\Record;
|
||||
|
||||
class Exports extends Controller {
|
||||
|
||||
public $requiredPermissions = ['martin.forms.access_exports'];
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
];
|
||||
|
||||
public $formConfig = 'config_form.yaml';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
BackendMenu::setContext('Martin.Forms', 'forms', 'exports');
|
||||
}
|
||||
|
||||
public function index() {
|
||||
$this->pageTitle = e(trans('martin.forms::lang.controllers.exports.title'));
|
||||
$this->create('frontend');
|
||||
}
|
||||
|
||||
public function csv() {
|
||||
|
||||
$records = Record::orderBy('created_at');
|
||||
|
||||
// FILTER GROUPS
|
||||
if (!empty($groups = post('Record.filter_groups'))) {
|
||||
$records->whereIn('group', $groups);
|
||||
}
|
||||
|
||||
// FILTER DATE
|
||||
if (!empty($date_after = post('Record.filter_date_after'))) {
|
||||
$records->whereDate('created_at', '>=', $date_after);
|
||||
}
|
||||
|
||||
// FILTER DATE
|
||||
if (!empty($date_before = post('Record.filter_date_before'))) {
|
||||
$records->whereDate('created_at', '<=', $date_before);
|
||||
}
|
||||
|
||||
// FILTER DELETED
|
||||
if ($deleted = post('Record.options_deleted')) {
|
||||
$records->withTrashed();
|
||||
}
|
||||
|
||||
// CREATE CSV
|
||||
$csv = CsvWriter::createFromFileObject(new SplTempFileObject());
|
||||
|
||||
// CHANGE DELIMTER
|
||||
if (post('Record.options_delimiter')) {
|
||||
$csv->setDelimiter(';');
|
||||
}
|
||||
|
||||
// SET UTF-8 Output
|
||||
if (post('Record.options_utf')) {
|
||||
$csv->setOutputBOM(AbstractCsv::BOM_UTF8);
|
||||
}
|
||||
|
||||
// CSV HEADERS
|
||||
$headers = [];
|
||||
|
||||
// METADATA HEADERS
|
||||
if (post('Record.options_metadata')) {
|
||||
$meta_headers = [
|
||||
e(trans('martin.forms::lang.controllers.records.columns.id')),
|
||||
e(trans('martin.forms::lang.controllers.records.columns.group')),
|
||||
e(trans('martin.forms::lang.controllers.records.columns.ip')),
|
||||
e(trans('martin.forms::lang.controllers.records.columns.created_at')),
|
||||
];
|
||||
$headers = array_merge($meta_headers, $headers);
|
||||
}
|
||||
|
||||
// ADD STORED FIELDS AS HEADER ROW IN CSV
|
||||
$filteredRecords = $records->get();
|
||||
$recordsArray = $filteredRecords->toArray();
|
||||
$record = $filteredRecords->first();
|
||||
$headers = array_merge($headers, array_keys($record->form_data_arr));
|
||||
|
||||
// ADD HEADERS
|
||||
$csv->insertOne($headers);
|
||||
|
||||
// WRITE CSV LINES
|
||||
foreach ($records->get()->toArray() as $row) {
|
||||
|
||||
$data = (array) json_decode($row['form_data']);
|
||||
|
||||
// IF DATA IS ARRAY CONVERT TO JSON STRING
|
||||
foreach ($data as $field => $value) {
|
||||
if (is_array($value) || is_object($value)) {
|
||||
$data[$field] = json_encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
// ADD METADATA IF NEEDED
|
||||
if (post('Record.options_metadata')) {
|
||||
array_unshift($data, $row['id'], $row['group'], $row['ip'], $row['created_at']);
|
||||
}
|
||||
|
||||
$csv->insertOne($data);
|
||||
|
||||
}
|
||||
|
||||
// RETURN CSV
|
||||
$csv->output('records.csv');
|
||||
exit();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Controllers;
|
||||
|
||||
use App, BackendMenu, Lang;
|
||||
use Backend\Classes\Controller;
|
||||
use Backend\Facades\Backend;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use October\Rain\Support\Facades\Flash;
|
||||
use Martin\Forms\Classes\GDPR;
|
||||
use Martin\Forms\Classes\UnreadRecords;
|
||||
use Martin\Forms\Models\Record;
|
||||
|
||||
class Records extends Controller {
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.ListController'
|
||||
];
|
||||
|
||||
public $listConfig = 'config_list.yaml';
|
||||
|
||||
public $requiredPermissions = ['martin.forms.access_records'];
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
BackendMenu::setContext('Martin.Forms', 'forms', 'records');
|
||||
}
|
||||
|
||||
public function view($id) {
|
||||
$record = Record::find($id);
|
||||
if(!$record) {
|
||||
Flash::error(e(trans('martin.forms::lang.controllers.records.error')));
|
||||
return Redirect::to(Backend::url('martin/forms/records'));
|
||||
}
|
||||
$record->unread = false;
|
||||
$record->save();
|
||||
$this->addCss('/plugins/martin/forms/assets/css/records.css');
|
||||
$this->pageTitle = e(trans('martin.forms::lang.controllers.records.view_title'));
|
||||
$this->vars['record'] = $record;
|
||||
}
|
||||
|
||||
public function onDelete() {
|
||||
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
|
||||
Record::whereIn('id',$checkedIds)->delete();
|
||||
}
|
||||
$counter = UnreadRecords::getTotal();
|
||||
return [
|
||||
'counter' => ($counter != null) ? $counter : 0,
|
||||
'list' => $this->listRefresh()
|
||||
];
|
||||
}
|
||||
|
||||
public function onDeleteSingle() {
|
||||
$id = post('id' );
|
||||
$record = Record::find($id);
|
||||
if($record) {
|
||||
$record->delete();
|
||||
Flash::success(e(trans('martin.forms::lang.controllers.records.deleted')));
|
||||
} else {
|
||||
Flash::error(e(trans('martin.forms::lang.controllers.records.error')));
|
||||
}
|
||||
return Redirect::to(Backend::url('martin/forms/records'));
|
||||
}
|
||||
|
||||
public function download($record_id, $file_id) {
|
||||
$record = Record::findOrFail($record_id);
|
||||
$file = $record->files->find($file_id);
|
||||
if(!$file) { App::abort(404, Lang::get('backend::lang.import_export.file_not_found_error')); }
|
||||
return response()->download($file->getLocalPath(), $file->getFilename());
|
||||
exit();
|
||||
}
|
||||
|
||||
public function listInjectRowClass($record, $definition = null) {
|
||||
if ($record->unread) {
|
||||
return 'new';
|
||||
}
|
||||
}
|
||||
|
||||
public function onReadState() {
|
||||
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
|
||||
$unread = (post('state') == 'read') ? 0 : 1;
|
||||
Record::whereIn('id',$checkedIds)->update(['unread' => $unread]);
|
||||
}
|
||||
$counter = UnreadRecords::getTotal();
|
||||
return [
|
||||
'counter' => ($counter != null) ? $counter : 0,
|
||||
'list' => $this->listRefresh()
|
||||
];
|
||||
}
|
||||
|
||||
public function onGDPRClean() {
|
||||
if ($this->user->hasPermission(['martin.forms.gdpr_cleanup'])) {
|
||||
GDPR::cleanRecords();
|
||||
Flash::success(e(trans('martin.forms::lang.controllers.records.alerts.gdpr_success')));
|
||||
} else {
|
||||
Flash::error(e(trans('martin.forms::lang.controllers.records.alerts.gdpr_perms')));
|
||||
}
|
||||
$counter = UnreadRecords::getTotal();
|
||||
return [
|
||||
'counter' => ($counter != null) ? $counter : 0,
|
||||
'list' => $this->listRefresh()
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
form : $/martin/forms/models/export/fields.yaml
|
||||
modelClass: Martin\Forms\Models\Record
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?php echo Backend::url('martin/forms/records') ?>"><?php echo e(trans('martin.forms::lang.menu.records.label')) ?></a></li>
|
||||
<li><?php echo e(trans('martin.forms::lang.controllers.exports.breadcrumb')) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php echo Form::open(['url' => Backend::url('martin/forms/exports/csv/')]) ?>
|
||||
|
||||
<?php echo $this->formRender() ?>
|
||||
|
||||
<div class="form-buttons m-t-lg">
|
||||
<?= Form::submit(e(trans('martin.forms::lang.controllers.exports.title')), ['class' => 'btn btn-primary']) ?>
|
||||
</div>
|
||||
|
||||
<?php echo Form::close() ?>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
scopes:
|
||||
|
||||
group:
|
||||
label : martin.forms::lang.controllers.records.columns.group
|
||||
type : group
|
||||
modelClass: Martin\Forms\Models\Record
|
||||
options : filterGroups
|
||||
conditions: "`group` in (:filtered)"
|
||||
|
||||
created_at:
|
||||
label : martin.forms::lang.controllers.records.columns.created_at
|
||||
type : daterange
|
||||
conditions: created_at >= ':after' AND created_at <= ':before'
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
list : $/martin/forms/models/record/columns.yaml
|
||||
modelClass : Martin\Forms\Models\Record
|
||||
title : martin.forms::lang.controllers.records.title
|
||||
recordUrl : martin/forms/records/view/:id
|
||||
noRecordsMessage: backend::lang.list.no_records
|
||||
recordsPerPage : 20
|
||||
showSetup : true
|
||||
showSorting : true
|
||||
showCheckboxes : true
|
||||
filter : config_filter.yaml
|
||||
|
||||
defaultSort:
|
||||
column : created_at
|
||||
direction: desc
|
||||
|
||||
toolbar:
|
||||
buttons: partials/list_toolbar
|
||||
search :
|
||||
prompt: backend::lang.list.search_prompt
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?= $this->listRender() ?>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<button type="button"
|
||||
class="btn btn-<?php echo $type ?> <?php echo $icon ?>"
|
||||
<?php if (!empty($needs_selected)): ?>
|
||||
disabled="disabled"
|
||||
<?php endif ?>
|
||||
data-request="<?php echo $request ?>"
|
||||
data-request-confirm="<?php echo $request_confirm; ?>"
|
||||
data-request-success="<?php echo $request_success; ?>"
|
||||
<?php if (!empty($onclick)) : ?>
|
||||
onclick="<?php echo $onclick; ?>"
|
||||
<?php endif ?>
|
||||
<?php if (!empty($needs_selected)) : ?>
|
||||
data-trigger-action="enable"
|
||||
data-trigger=".control-list input[type=checkbox]"
|
||||
data-trigger-condition="checked"
|
||||
<?php endif ?>
|
||||
data-stripe-load-indicator>
|
||||
<?php echo $label; ?>
|
||||
</button>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<div id="records-toolbar" data-control="toolbar">
|
||||
|
||||
<?php
|
||||
echo $this->makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
|
||||
'type' => 'danger',
|
||||
'icon' => 'oc-icon-trash',
|
||||
'label' => e(trans('backend::lang.list.delete_selected')),
|
||||
'onclick' => "$(this).data('request-data', { checked: $('.control-list').listWidget('getChecked') })",
|
||||
'needs_selected' => true,
|
||||
'request' => 'onDelete',
|
||||
'request_confirm' => e(trans('backend::lang.list.delete_selected_confirm')),
|
||||
'request_success' => "
|
||||
$('#records-toolbar').find('button').prop('disabled', true);
|
||||
$.oc.flashMsg({
|
||||
'text': '" . e(trans('backend::lang.list.delete_selected_success')) . "',
|
||||
'class': 'success',
|
||||
'interval': 3
|
||||
});
|
||||
$.oc.sideNav.setCounter('forms/records', data.counter);
|
||||
$('#Lists').html(data.list['#Lists']);
|
||||
",
|
||||
]);
|
||||
?>
|
||||
|
||||
<div class="btn-group">
|
||||
<?php
|
||||
echo $this->makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
|
||||
'type' => 'default',
|
||||
'icon' => 'oc-icon-eye-slash',
|
||||
'label' => e(trans('martin.forms::lang.controllers.records.buttons.unread')),
|
||||
'onclick' => "$(this).data('request-data', { state: 'unread', checked: $('.control-list').listWidget('getChecked') })",
|
||||
'needs_selected' => true,
|
||||
'request' => 'onReadState',
|
||||
'request_confirm' => '',
|
||||
'request_success' => "
|
||||
$('#records-toolbar').find('button').prop('disabled', true);
|
||||
$.oc.sideNav.setCounter('forms/records', data.counter);
|
||||
$('#Lists').html(data.list['#Lists']);
|
||||
",
|
||||
]);
|
||||
|
||||
echo $this->makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
|
||||
'type' => 'default',
|
||||
'icon' => 'oc-icon-eye',
|
||||
'label' => e(trans('martin.forms::lang.controllers.records.buttons.read')),
|
||||
'onclick' => "$(this).data('request-data', { state: 'read', checked: $('.control-list').listWidget('getChecked') })",
|
||||
'needs_selected' => true,
|
||||
'request' => 'onReadState',
|
||||
'request_confirm' => '',
|
||||
'request_success' => "
|
||||
$('#records-toolbar').find('button').prop('disabled', true);
|
||||
$.oc.sideNav.setCounter('forms/records', data.counter);
|
||||
$('#Lists').html(data.list['#Lists']);
|
||||
",
|
||||
]);
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if(Martin\Forms\Models\Settings::get('gdpr_enable')) {
|
||||
echo $this->makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
|
||||
'type' => 'danger',
|
||||
'icon' => 'oc-icon-history',
|
||||
'label' => e(trans('martin.forms::lang.controllers.records.buttons.gdpr_clean')),
|
||||
'request' => 'onGDPRClean',
|
||||
'request_confirm' => e(trans('martin.forms::lang.controllers.records.alerts.gdpr_confirm')),
|
||||
'request_success' => "
|
||||
$('#records-toolbar').find('button').blur();
|
||||
$.oc.sideNav.setCounter('forms/records', data.counter);
|
||||
$('#Lists').html(data.list['#Lists']);
|
||||
",
|
||||
]);
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<div class="control-toolbar">
|
||||
<div class="toolbar-item toolbar-primary">
|
||||
<div data-control="toolbar">
|
||||
<a href="<?= Backend::url('martin/forms/records') ?>" type="button" class="btn btn-default oc-icon-arrow-left"><?= e(trans('backend::lang.form.return_to_list')) ?></a>
|
||||
<button type="button" class="btn btn-danger oc-icon-trash"
|
||||
data-request="onDeleteSingle"
|
||||
data-request-confirm="<?= e(trans('backend::lang.form.confirm_delete')) ?>"
|
||||
data-request-data="id: <?= $record->id ?>"
|
||||
data-stripe-load-indicator
|
||||
><?= e(trans('backend::lang.account.delete')) ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?php echo Backend::url('martin/forms/records') ?>"><?php echo e(trans('martin.forms::lang.menu.records.label')) ?></a></li>
|
||||
<li>Record #<?php echo $record->id ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?= $this->makePartial('partials/view_toolbar') ?>
|
||||
|
||||
<h2 class="text-center">Record #<?php echo $record->id ?></h2>
|
||||
|
||||
<table class="record-table m-t-md">
|
||||
<?php foreach($record->form_data_arr as $label => $value): ?>
|
||||
<tr>
|
||||
<td class="record-label"><?php echo ucwords($label) ?>:</td>
|
||||
<td class="record-value">
|
||||
<?php if(is_array($value) || is_object($value)): ?>
|
||||
<ul>
|
||||
<?php echo \Martin\Forms\Classes\BackendHelpers::array2ul($value) ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<?php echo nl2br(htmlspecialchars($value, ENT_QUOTES)); ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<?php if(count($record->files) > 0): ?>
|
||||
<tr>
|
||||
<td class="record-label">Attached Files:</td>
|
||||
<td class="record-value files-container">
|
||||
<ul>
|
||||
<?php foreach($record->files as $file): ?>
|
||||
<li>
|
||||
<a href="<?php echo Backend::url('martin/forms/records/download', [$record->id, $file->id]) ?>">
|
||||
<?php echo $file->file_name ?>
|
||||
(<?php echo $file->sizeToString() ?>)
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
</table>
|
||||
|
||||
<div class="record-metadata m-t-md m-r-md text-right">
|
||||
<p class="m-a-0"><?php echo e(trans('martin.forms::lang.controllers.records.columns.group')) ?>: <?php echo $record->group ?></p>
|
||||
<p class="m-a-0"><?php echo e(trans('martin.forms::lang.controllers.records.columns.ip')) ?>: <?php echo $record->ip ?></p>
|
||||
<p class="m-a-0"><?php echo e(trans('martin.forms::lang.controllers.records.columns.created_at')) ?>: <?php echo $record->created_at ?></p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'plugin' => [
|
||||
'name' => 'Magic Forms',
|
||||
'description' => 'einfaches Erstellen von AJAX Formularen'
|
||||
],
|
||||
|
||||
'menu' => [
|
||||
'label' => 'Magic Forms',
|
||||
'records' => ['label' => 'Records'],
|
||||
'exports' => ['label' => 'Export'],
|
||||
'settings' => 'Konfigurieren der plugin parameter',
|
||||
],
|
||||
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => 'Einträge anzeigen',
|
||||
'view_title' => 'Details zum Eintrag',
|
||||
'error' => 'Eintrag nicht gefunden',
|
||||
'deleted' => 'Eintrag erfolgreich gelöscht',
|
||||
'columns' => [
|
||||
'id' => 'Eintrag ID',
|
||||
'group' => 'Gruppe',
|
||||
'ip' => 'IP Adresse',
|
||||
'form_data' => 'Gespeicherte Felder',
|
||||
'files' => 'Anhänge',
|
||||
'created_at' => 'Erstellt',
|
||||
],
|
||||
'buttons' => [
|
||||
'read' => 'Als gelesen markieren',
|
||||
'unread' => 'Als ungelesen markieren',
|
||||
'gdpr_clean' => 'DSVGO Aufräumung',
|
||||
],
|
||||
'alerts' => [
|
||||
'gdpr_confirm' => "Sind sie sicher dass Sie alte Einträge aufräumen wollen?\nDiese Aktion kann nicht wiederrufen werden!",
|
||||
'gdpr_success' => 'DSVGO Aufräumung wurde erfolgreich ausgeführt',
|
||||
'gdpr_perms' => 'Sie haben keine Berechtigung für diese Funktion.',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Einträge exportieren',
|
||||
'breadcrumb' => 'Exportieren',
|
||||
'filter_section' => '1. Einträge filtern',
|
||||
'filter_type' => 'Alle Einträge exportieren',
|
||||
'filter_groups' => 'Gruppen',
|
||||
'filter_date_after' => 'Nach dem Datum',
|
||||
'filter_date_before' => 'Vor dem Datum',
|
||||
'options_section' => '2. Extra Optionen',
|
||||
'options_metadata' => 'Inklusive Metadaten',
|
||||
'options_metadata_com' => 'Exportiere die Einträge mit Metadaten (Eintrag ID, Gruppe, IP, Erstellungsdatum)',
|
||||
'options_deleted' => 'Inklusive gelöschter Einträge',
|
||||
'options_delimiter' => 'Alternatives Trennzeichen benutzen',
|
||||
'options_delimiter_com' => 'Semikolon als Trennzeichen',
|
||||
'options_utf' => 'Enkodierung in UTF8',
|
||||
'options_utf_com' => 'Enkodierung Ihrer CSV-Datei im UTF-8 Format für die Unterstützung von Umlauten und Sonderzeichen.',
|
||||
],
|
||||
],
|
||||
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => 'Generisches AJAX Formular',
|
||||
'description' => 'Mit Standardeinstellungen rendered ein generisches Formular; Überschreib Komponenten HTML mit deinen eigenen Feldern.',
|
||||
],
|
||||
'upload_form' => [
|
||||
'name' => 'Upload AJAX Formular [BETA]',
|
||||
'description' => 'Zeigt wie man Dateiupload in deinem Formular implementiert.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => 'Leeres AJAX Formular',
|
||||
'description' => 'Erstellt eine leere Vorlage für dein individuelles Formular; Überschreib Komponenten HTML.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'Formular Seitzung abgelaufen! Bitte die Seite neuladen.',
|
||||
'recaptcha_warn' => 'Warnung: reCAPTCHA ist nicht ordentlich konfiguriert. Bitte, gehe zum Backend > Einstellungen > CMS > Magic Forms um reCAPTCHA zu kofigurieren.',
|
||||
'group_validation' => 'Formular Validierung',
|
||||
'group_messages' => 'Flash Benachrichtung',
|
||||
'group_mail' => 'Benachrichtigungen Einstellungen',
|
||||
'group_mail_resp' => 'Automatische Antwort Einstellungen',
|
||||
'group_settings' => 'Weitere Einstellungen',
|
||||
'group_security' => 'Sicherheit',
|
||||
'group_recaptcha' => 'reCAPTCHA Einstellungen',
|
||||
'group_advanced' => 'Fortgeschrittene Einstellungen',
|
||||
'group_uploader' => 'Uploader Einstellungen',
|
||||
'validation_req' => 'Die Eigenschaft wird benötigt',
|
||||
'group' => ['title' => 'Gruppe' , 'description' => 'Organisiere deine Formulare durch eigene Gruppennamen. Diese Option ist für das Exportieren der Daten sehr praktischt.'],
|
||||
'rules' => ['title' => 'Regeln' , 'description' => 'Erstelle eigene Regeln Mithilfe der Laravel Validierungsfunktion'],
|
||||
'rules_messages' => ['title' => 'Regeln Benachrichtigungen' , 'description' => 'Erstelle eigene Benachrichtigungen Mithilfe der Laravel Validierungsfunktion'],
|
||||
'custom_attributes' => ['title' => 'Eigene Attribute' , 'description' => 'Erstelle eigene Attribute Mithilfe der Laravel Validierungsfunktion'],
|
||||
'messages_success' => ['title' => 'Erfolg' , 'description' => 'Nachricht bei erfolgreicher Übermittlung des Formulars', 'default' => 'Your form was successfully submitted' ],
|
||||
'messages_errors' => ['title' => 'Fehler' , 'description' => 'Nachricht bei Fehlern während der Eingabe der Formulardaten' , 'default' => 'There were errors with your submission'],
|
||||
'messages_partial' => ['title' => 'Benutze eigene Partial' , 'description' => 'Überschreibe Flash-Nachrichten mit deinem eigenem Partial in deinem Theme'],
|
||||
'mail_enabled' => ['title' => 'Sende Benachrichtigungen' , 'description' => 'Sende eine Benachrichtigungsmail nach jeder erfolgreichen Übertragung.'],
|
||||
'mail_subject' => ['title' => 'Betreff' , 'description' => 'Überschreibe die standard E-Mail Betreffszeile'],
|
||||
'mail_recipients' => ['title' => 'Empfänger' , 'description' => 'E-Mail Empfänger eintragen (Füge eine E-Mail Adresse pro Zeile ein)'],
|
||||
'mail_bcc' => ['title' => 'BCC' , 'description' => 'Sende blind carbon copy (BCC) zur folgenden Empfängern (Füge eine E-Mail Adresse per Zeile ein)'],
|
||||
'mail_replyto' => ['title' => 'Empfänger E-Mail Feld', 'description' => 'Formular Feld das die E-Mail Adresse enthält. Diese Adresse wird als Empfänger Adresse benutzt.'],
|
||||
'mail_template' => ['title' => 'E-Mail Vorlage' , 'description' => 'Benutze eigene E-Mail Vorlagen. Gebe einen Vorlagen Code ein wie z.B. "martin.forms::mail.notification" (Kann im Einstellungen der E-Mail Vorlagen im Backend gefunden werden). Leer lassen für Standardvorlage.'],
|
||||
'mail_uploads' => ['title' => 'Sende Uploads' , 'description' => 'Sende Uploads als Anhang'],
|
||||
'mail_resp_enabled' => ['title' => 'Sende Auto-Antwort' , 'description' => 'Sende eine automatische Antwort E-Mail zu der Person die dein Formular ausgfüllt.'],
|
||||
'mail_resp_field' => ['title' => 'E-Mail Feld' , 'description' => 'Formular Feld das die E-Mail Adresse enthät für die automatische Antwort'],
|
||||
'mail_resp_from' => ['title' => 'Absender Adresse' , 'description' => 'Absender E-Mail Adresse was benutzt werden soll (z.B. noreply@yourcompany.com)'],
|
||||
'mail_resp_subject' => ['title' => 'Betreff' , 'description' => 'Überschreibe standard E-Mail Betreffszeile'],
|
||||
'reset_form' => ['title' => 'Resete Formular' , 'description' => 'Resete das Formular nach erfolgreichen Übertragung'],
|
||||
'redirect' => ['title' => 'Umleitung bei Erfolg', 'description' => 'URL-Umleitung nach erfoglreichen Übertragung'],
|
||||
'inline_errors' => ['title' => 'Inline Fehler' , 'description' => 'Zeige Inline-Fehler an. Die Funktion braucht Extra-Code. Siehe in der Dokumentation für mehr Informationen.', 'disabled' => 'Deaktiviert', 'display' => 'Zeige Fehler', 'variable' => 'JS Variable'],
|
||||
'js_on_success' => ['title' => 'JS bei Erfolg' , 'description' => 'Führe eigenen JavaScript-Code aus wenn das Formular erfolgreich übertragen wurde. Keine Script Tags benutzen!'],
|
||||
'js_on_error' => ['title' => 'JS bei Fehlern' , 'description' => 'Führe eigenen JavaScript-Code aus wenn das Formular nicht validiert werden kann. Keine Script Tags benutzen!'],
|
||||
'allowed_fields' => ['title' => 'Erlaubte Felder' , 'description' => 'Festlegen welche Felder gefiltert und gespeichert werden sollen. (Füge ein Feld per Zeile ein)'],
|
||||
'anonymize_ip' => ['title' => 'IP-Anonymisierung' , 'description' => 'IP-Adresse nicht speichern.', 'full' => 'Voll', 'partial' => 'Teilanonymisierung', 'disabled' => 'Deaktiviert'],
|
||||
'sanitize_data' => ['title' => 'Bereinigung der Formulardaten' , 'description' => 'Bereinige die Formulardaten und speichere das Ergebnis in der Datenbank', 'disabled' => 'deaktiviert', 'htmlspecialchars' => 'Benutzer htmlspecialchars'],
|
||||
'recaptcha_enabled' => ['title' => 'Aktiviere reCAPTCHA' , 'description' => 'reCAPTCHA-Widget in deinem Formular einfügen'],
|
||||
'recaptcha_theme' => ['title' => 'Theme' , 'description' => 'Farbschema des Widgets', 'light' => 'Hell' , 'dark' => 'Dunkel'],
|
||||
'recaptcha_type' => ['title' => 'Typ' , 'description' => 'reCAPTCHA Typ festlegen' , 'image' => 'Bild' , 'audio' => 'Audio'],
|
||||
'recaptcha_size' => [
|
||||
'title' => 'Größe',
|
||||
'description' => 'Die Größe des Widgets',
|
||||
'normal' => 'Normal',
|
||||
'compact' => 'Kompakt',
|
||||
'invisible' => 'Unsichtbar',
|
||||
],
|
||||
'skip_database' => ['title' => 'Überspringe DB' , 'description' => 'Keine Speicherung der Formulardaten in der Datenbank. Nützlich wenn man Events mit einem eigenem Plugin nutzen möchte.'],
|
||||
'emails_date_format' => ['title' => 'Datum Formatierung in E-Mails', 'description' => 'Ändere die Datumformatierung die dann in E-Mails verwendet wird.'],
|
||||
'uploader_enable' => ['title' => 'Erlaube Uploads' , 'description' => 'Aktiviere Datei-Upload. Diese Option muss aufgrund der Sicherheitseinstellungen explizit aktiviert werden.'],
|
||||
'uploader_multi' => ['title' => 'Merhfachdateien' , 'description' => 'Erlaube Mehrfachdateien-Uploads'],
|
||||
'uploader_pholder' => ['title' => 'Platzhalter Text' , 'description' => 'Platzhalter Text der angezeigt wird so lange keine Datei hochgeladen wurde', 'default' => 'Click or drag files to upload'],
|
||||
'uploader_maxsize' => ['title' => 'Dateigröße Limitierung' , 'description' => 'Die maximale Dateigröße die hochgeladen werden kann in Megabytes'],
|
||||
'uploader_types' => ['title' => 'Erlaubte Datei-Typen' , 'description' => 'Erlaubte Dateiendungen oder ein Stern (*) für alle Typen (Füge eine Dateiendung per Zeile ein)'],
|
||||
'uploader_remFile' => ['title' => 'Popup Text Datei entfernen' , 'description' => 'Text Platzhalter für die Abfrage, wenn eine Datei vor dem Upload entfernt wird', 'default' => 'Are you sure ?'],
|
||||
]
|
||||
],
|
||||
|
||||
'settings' => [
|
||||
'tabs' => ['general' => 'Allgemein', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'DSVGO'],
|
||||
'section_flash_messages' => 'Flash Nachrichten',
|
||||
'global_messages_success' => ['label' => 'Globale Erfolgsnachricht', 'comment' => '(Diese Einstellung kann aus der Komponente heraus überschrieben werden)', 'default' => 'Your form was successfully submitted'],
|
||||
'global_messages_errors' => ['label' => 'Globale Fehlernachricht' , 'comment' => '(Diese Einstellung kann aus der Komponente heraus überschrieben werden)', 'default' => 'There were errors with your submission'],
|
||||
'plugin_help' => 'Die Plugindokumentation erreichst Du über die GitHub repo:',
|
||||
'global_hide_button' => 'Navigationsobjekt vestecken',
|
||||
'global_hide_button_desc' => 'Praktisch, wenn Du eigene Events mit einem eigenem Plugin nutzen möchtest.',
|
||||
'section_recaptcha' => 'reCAPTCHA Einstellungen',
|
||||
'recaptcha_site_key' => 'Site key',
|
||||
'recaptcha_secret_key' => 'Secret key',
|
||||
'gdpr_help_title' => 'Information',
|
||||
'gdpr_help_comment' => 'Das neue EU-DSVGO Gesetz in Europa besagt dass die Einträge nicht mehr unendlich aufbewahrt werden dürfen. Diese müssen je nach nach Bedarf automatisiert gelöscht werden.',
|
||||
'gdpr_enable' => 'Aktiviere EU-DSVGO konforme Aufräumung',
|
||||
'gdpr_days' => 'Behalte die Einträge für die Anzahl an: X Tagen',
|
||||
],
|
||||
|
||||
'permissions' => [
|
||||
'tab' => 'Magic Forms',
|
||||
'access_records' => 'Zugriff auf gespeicherte Formular-Einsendedaten',
|
||||
'access_exports' => 'Zugriff für das Exportieren der gespeicherten Formular-Einsendedaten',
|
||||
'access_settings' => 'Zugriff auf Modul-Konfiguration',
|
||||
'gdpr_cleanup' => 'Führe EU-DSVGO Aufräumung der Datenbank durch.',
|
||||
],
|
||||
|
||||
'mails' => [
|
||||
'form_notification' => ['description' => 'Benachrichtung nach erfolgreicher Einsendung der Formulardaten.'],
|
||||
'form_autoresponse' => ['description' => 'Automatische Antwort nach erfolgreicher Einsendung der Formulardaten.'],
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'Kann das reCAPTCHA Feld nicht validieren.'
|
||||
],
|
||||
|
||||
'classes' => [
|
||||
'GDPR' => [
|
||||
'alert_gdpr_disabled' => 'EU-DSVGO Einstellungen sind deaktiviert.',
|
||||
'alert_invalid_gdpr' => 'Ungültige Einstellung des EU-DSVGO Aufräumvorgangs Intervals nach X Tagen.',
|
||||
]
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'plugin' => [
|
||||
'name' => 'Magic Forms',
|
||||
'description' => 'Create easy AJAX forms'
|
||||
],
|
||||
|
||||
'menu' => [
|
||||
'label' => 'Magic Forms',
|
||||
'records' => ['label' => 'Records'],
|
||||
'exports' => ['label' => 'Export'],
|
||||
'settings' => 'Configure plugin parameters',
|
||||
],
|
||||
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => 'View Records',
|
||||
'view_title' => 'Record Details',
|
||||
'error' => 'Record not found',
|
||||
'deleted' => 'Record deleted successfully',
|
||||
'columns' => [
|
||||
'id' => 'Record ID',
|
||||
'group' => 'Group',
|
||||
'ip' => 'IP Address',
|
||||
'form_data' => 'Stored Fields',
|
||||
'files' => 'Attached Files',
|
||||
'created_at' => 'Created',
|
||||
],
|
||||
'buttons' => [
|
||||
'read' => 'Mark as Read',
|
||||
'unread' => 'Mark as Unread',
|
||||
'gdpr_clean' => 'GDPR Clean',
|
||||
],
|
||||
'alerts' => [
|
||||
'gdpr_confirm' => "Are you sure you want to clean old records?\nThis action cannot be undone!",
|
||||
'gdpr_success' => 'GDPR cleanup was executed successfully',
|
||||
'gdpr_perms' => 'You don\'t have permission to this feature',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Export Records',
|
||||
'breadcrumb' => 'Export',
|
||||
'filter_section' => '1. Filter records',
|
||||
'filter_type' => 'Export all records',
|
||||
'filter_groups' => 'Groups',
|
||||
'filter_date_after' => 'Date after',
|
||||
'filter_date_before' => 'Date before',
|
||||
'options_section' => '2. Extra options',
|
||||
'options_metadata' => 'Include metadata',
|
||||
'options_metadata_com' => 'Export records with metadata (Record ID, group, IP, created date)',
|
||||
'options_deleted' => 'Include deleted records',
|
||||
'options_delimiter' => 'Use alternative delimiter',
|
||||
'options_delimiter_com' => 'Use semicolon as delimiter',
|
||||
'options_utf' => 'Encode in UTF8',
|
||||
'options_utf_com' => 'Encode your csv in UTF-8 to support non standard characters',
|
||||
],
|
||||
],
|
||||
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => 'Generic AJAX Form',
|
||||
'description' => 'By default renders a generic form; override component HTML with your custom fields.',
|
||||
],
|
||||
'upload_form' => [
|
||||
'name' => 'Upload AJAX Form [BETA]',
|
||||
'description' => 'Shows how to implement file uploads on your form.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => 'Empty AJAX Form',
|
||||
'description' => 'Create a empty template for your custom form; override component HTML.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'Form session expired! Please refresh the page.',
|
||||
'recaptcha_warn' => 'Warning: reCAPTCHA is not properly configured. Please, goto Backend > Settings > CMS > Magic Forms and configure.',
|
||||
'group_validation' => 'Form Validation',
|
||||
'group_messages' => 'Flash Messages',
|
||||
'group_mail' => 'Notifications Settings',
|
||||
'group_mail_resp' => 'Auto-Response Settings',
|
||||
'group_settings' => 'More Settings',
|
||||
'group_security' => 'Security',
|
||||
'group_recaptcha' => 'reCAPTCHA Settings',
|
||||
'group_advanced' => 'Advanced Settings',
|
||||
'group_uploader' => 'Uploader Settings',
|
||||
'validation_req' => 'The property is required',
|
||||
'group' => ['title' => 'Group' , 'description' => 'Organize your forms with a custom group name. This option is useful when exporting data.'],
|
||||
'rules' => ['title' => 'Rules' , 'description' => 'Set your own rules using Laravel validation'],
|
||||
'rules_messages' => ['title' => 'Rules Messages' , 'description' => 'Use your own rules messages using Laravel validation'],
|
||||
'custom_attributes' => ['title' => 'Custom Attributes' , 'description' => 'Use your own custom attributes using Laravel validation'],
|
||||
'messages_success' => ['title' => 'Success' , 'description' => 'Message when the form is successfully submited', 'default' => 'Your form was successfully submitted' ],
|
||||
'messages_errors' => ['title' => 'Errors' , 'description' => 'Message when the form contains errors' , 'default' => 'There were errors with your submission'],
|
||||
'messages_partial' => ['title' => 'Use Custom Partial' , 'description' => 'Override flash messages with your custom partial inside your theme'],
|
||||
'mail_enabled' => ['title' => 'Send Notifications' , 'description' => 'Send mail notifications on every form submited'],
|
||||
'mail_subject' => ['title' => 'Subject' , 'description' => 'Override default email subject'],
|
||||
'mail_recipients' => ['title' => 'Recipients' , 'description' => 'Specify email recipients (add one address per line)'],
|
||||
'mail_bcc' => ['title' => 'BCC' , 'description' => 'Send blind carbon copy to email recipients (add one address per line)'],
|
||||
'mail_replyto' => ['title' => 'ReplyTo Email Field', 'description' => 'Form field containing the email address of sender to be used as "ReplyTo"'],
|
||||
'mail_template' => ['title' => 'Mail Template' , 'description' => 'Use custom mail template. Specify template code like "martin.forms::mail.notification" (found on Settings, Mail templates). Leave empty to use default.'],
|
||||
'mail_uploads' => ['title' => 'Send Uploads' , 'description' => 'Send uploads as attachements'],
|
||||
'mail_resp_enabled' => ['title' => 'Send Auto-Response' , 'description' => 'Send an auto-response email to the person submitting the form'],
|
||||
'mail_resp_field' => ['title' => 'Email Field' , 'description' => 'Form field containing the email address of the recipient of auto-response'],
|
||||
'mail_resp_from' => ['title' => 'Sender Address' , 'description' => 'Email address of auto-response email sender (e.g. noreply@yourcompany.com)'],
|
||||
'mail_resp_subject' => ['title' => 'Subject' , 'description' => 'Override default email subject'],
|
||||
'reset_form' => ['title' => 'Reset Form' , 'description' => 'Reset form after successfully submit'],
|
||||
'redirect' => ['title' => 'Redirect on Success', 'description' => 'Redirect to URL on successfully submit.'],
|
||||
'inline_errors' => ['title' => 'Inline errors' , 'description' => 'Display inline errors. This requires extra code, check documentation for more info.', 'disabled' => 'Disabled', 'display' => 'Display errors', 'variable' => 'JS variable'],
|
||||
'js_on_success' => ['title' => 'JS on Success' , 'description' => 'Execute custom JavaScript code when the form was successfully submitted. Don\'t use script tags.'],
|
||||
'js_on_error' => ['title' => 'JS on Error' , 'description' => 'Execute custom JavaScript code when the form doesn\'t validate. Don\'t use script tags.'],
|
||||
'allowed_fields' => ['title' => 'Allowed Fields' , 'description' => 'Specify which fields should be filtered and stored (add one field name per line)'],
|
||||
'anonymize_ip' => ['title' => 'Anonymize IP' , 'description' => 'Don\'t store IP address', 'full' => 'Full', 'partial' => 'Partial', 'disabled' => 'Disabled'],
|
||||
'sanitize_data' => ['title' => 'Sanitize form data' , 'description' => 'Sanitize form data and save result on database', 'disabled' => 'Disabled', 'htmlspecialchars' => 'Use htmlspecialchars'],
|
||||
'recaptcha_enabled' => ['title' => 'Enable reCAPTCHA' , 'description' => 'Insert the reCAPTCHA widget on your form'],
|
||||
'recaptcha_theme' => ['title' => 'Theme' , 'description' => 'The color theme of the widget', 'light' => 'Light' , 'dark' => 'Dark'],
|
||||
'recaptcha_type' => ['title' => 'Type' , 'description' => 'The type of CAPTCHA to serve' , 'image' => 'Image' , 'audio' => 'Audio'],
|
||||
'recaptcha_size' => [
|
||||
'title' => 'Size',
|
||||
'description' => 'The size of the widget',
|
||||
'normal' => 'Normal',
|
||||
'compact' => 'Compact',
|
||||
'invisible' => 'Invisible',
|
||||
],
|
||||
'skip_database' => ['title' => 'Skip DB' , 'description' => 'Don\'t store this form on database. Useful if you want to use events with your custom plugin.'],
|
||||
'emails_date_format' => ['title' => 'Date format on emails', 'description' => 'Set custom format for dates used on emails subjects.'],
|
||||
'uploader_enable' => ['title' => 'Allow uploads' , 'description' => 'Enable files uploading. You need to explicitly enable this option as a security measure.'],
|
||||
'uploader_multi' => ['title' => 'Multiple files' , 'description' => 'Allow multipe files uploads'],
|
||||
'uploader_pholder' => ['title' => 'Placeholder text' , 'description' => 'Wording to display when no file is uploaded', 'default' => 'Click or drag files to upload'],
|
||||
'uploader_maxsize' => ['title' => 'File size limit' , 'description' => 'The maximum file size that can be uploaded in megabytes'],
|
||||
'uploader_types' => ['title' => 'Allowed file types' , 'description' => 'Allowed file extensions or star (*) for all types (add one extension per line)'],
|
||||
'uploader_remFile' => ['title' => 'Remove Popup text' , 'description' => 'Wording to display in the popup when you remove file', 'default' => 'Are you sure ?'],
|
||||
]
|
||||
],
|
||||
|
||||
'settings' => [
|
||||
'tabs' => ['general' => 'General', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'GDPR'],
|
||||
'section_flash_messages' => 'Flash Messages',
|
||||
'global_messages_success' => ['label' => 'Global Success Message', 'comment' => '(This setting can be overridden from the component)', 'default' => 'Your form was successfully submitted'],
|
||||
'global_messages_errors' => ['label' => 'Global Errors Message' , 'comment' => '(This setting can be overridden from the component)', 'default' => 'There were errors with your submission'],
|
||||
'plugin_help' => 'You can access plugin documentation at GitHub repo:',
|
||||
'global_hide_button' => 'Hide navigation item',
|
||||
'global_hide_button_desc' => 'Useful if you want to use events with your custom plugin.',
|
||||
'section_recaptcha' => 'reCAPTCHA Settings',
|
||||
'recaptcha_site_key' => 'Site key',
|
||||
'recaptcha_secret_key' => 'Secret key',
|
||||
'gdpr_help_title' => 'Information',
|
||||
'gdpr_help_comment' => 'New GDPR law in Europe, you can\'t keep records undefinitely, need to clear them after a certain period of time depending on your needs',
|
||||
'gdpr_enable' => 'Enable GDPR',
|
||||
'gdpr_days' => 'Keep records for a maximum of X days',
|
||||
],
|
||||
|
||||
'permissions' => [
|
||||
'tab' => 'Magic Forms',
|
||||
'access_records' => 'Access stored forms data',
|
||||
'access_exports' => 'Access to export stored data',
|
||||
'access_settings' => 'Access module configuration',
|
||||
'gdpr_cleanup' => 'Perform GDPR database cleanup',
|
||||
],
|
||||
|
||||
'mails' => [
|
||||
'form_notification' => ['description' => 'Notify when a form is submited'],
|
||||
'form_autoresponse' => ['description' => 'Auto-Response when a form is submited'],
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'Cannot validate reCAPTCHA field'
|
||||
],
|
||||
|
||||
'classes' => [
|
||||
'GDPR' => [
|
||||
'alert_gdpr_disabled' => 'GDPR options are disabled',
|
||||
'alert_invalid_gdpr' => 'Invalid GDPR days setting value',
|
||||
]
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
return [
|
||||
'plugin' => [
|
||||
'name' => 'Magic Forms',
|
||||
'description' => 'Créer des formulaires AJAX facilement'
|
||||
],
|
||||
'menu' => [
|
||||
'label' => 'Magic Forms',
|
||||
'records' => ['label' => 'Enregistrements'],
|
||||
'exports' => ['label' => 'Export'],
|
||||
'settings' => 'Configurer kes parameters du plugin',
|
||||
],
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => 'Voir les enregistrements',
|
||||
'view_title' => 'Détails de l\'enregistrement',
|
||||
'error' => 'Enregistrement non trouvé',
|
||||
'deleted' => 'Enregistrement supprimé avec succès',
|
||||
'columns' => [
|
||||
'id' => 'Enregistrement N°',
|
||||
'group' => 'Groupe',
|
||||
'ip' => 'Adresse IP',
|
||||
'form_data' => 'Champs stockés',
|
||||
'files' => 'Fichiers attachés',
|
||||
'created_at' => 'Créer le',
|
||||
],
|
||||
'buttons' => [
|
||||
'read' => 'Marquer comme lu',
|
||||
'unread' => 'Marquer comme non lu',
|
||||
'gdpr_clean' => 'Nettoyage RGPD',
|
||||
],
|
||||
'alerts' => [
|
||||
'gdpr_confirm' => "Êtes-vous sûr de vouloir nettoyer les anciens enregistrements?\nCette action ne peut pas être annulée!",
|
||||
'gdpr_success' => 'Le nettoyage RGPD a été exécuté avec succès',
|
||||
'gdpr_perms' => 'Vous n\'avez pas l\'autorisation pour utiliser cette fonctionnalité',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Exporter les enregistrements',
|
||||
'breadcrumb' => 'Exporter',
|
||||
'filter_section' => '1. Filtrer les enregistrements',
|
||||
'filter_type' => 'Exporter tous les enregistrements',
|
||||
'filter_groups' => 'Groupes',
|
||||
'filter_date_after' => 'Date de début',
|
||||
'filter_date_before' => 'Date de fin',
|
||||
'options_section' => '2. Options supplémentaires',
|
||||
'options_metadata' => 'Inclure les metadonnées',
|
||||
'options_metadata_com' => 'Exporter les enregistrements avec les metadonnées (n°, groupe, IP, date de création)',
|
||||
'options_deleted' => 'Inclure les enregistrements supprimés',
|
||||
],
|
||||
],
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => 'Formulaire générique AJAX',
|
||||
'description' => 'Rendu d\'un formulaire générique; remplacer le composant HTML par vos propres champs personnalisés.',
|
||||
],
|
||||
'upload_form' => [
|
||||
'name' => 'Téléchargements AJAX [BETA]',
|
||||
'description' => 'Montre comment implémenter des téléchargements de fichiers sur votre formulaire.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => 'Formulaire AJAX vide',
|
||||
'description' => 'Créer un modèle vide pour votre formulaire personnalisé; remplacer le composant HTML.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'La session du formulaire a expirée ! Veuillez actualiser la page.',
|
||||
'recaptcha_warn' => 'Avertissement: reCAPTCHA n\'est pas correctement configuré. Àllez dans Backend > Paramètres > CMS > Magic Forms et configurez svp.',
|
||||
'group_validation' => 'Validation du formulaire',
|
||||
'group_messages' => 'Messages Flash ',
|
||||
'group_mail' => 'Notifications',
|
||||
'group_mail_resp' => 'Réponse automatique',
|
||||
'group_settings' => 'Plus de réglages',
|
||||
'group_security' => 'Securité',
|
||||
'group_recaptcha' => 'reCAPTCHA',
|
||||
'group_uploader' => 'Transfert de fichiers',
|
||||
'validation_req' => 'La propriété est requise',
|
||||
'group' => ['title' => 'Groupe' , 'description' => 'Organisez vos formulaires avec un nom de groupe personnalisé. Cette option est utile lorsque vous exportez des données.'],
|
||||
'rules' => ['title' => 'Règles' , 'description' => 'Définissez vos propres règles en utilisant la validation de Laravel'],
|
||||
'rules_messages' => ['title' => 'Messages de règles' , 'description' => 'Utilisez vos propres messages de règles en utilisant la validation de Laravel'],
|
||||
'messages_success' => ['title' => 'Succès' , 'description' => 'Message lorsque le formulaire est soumis avec succès' , 'default' => 'Votre formulaire a été envoyé avec succès' ],
|
||||
'messages_errors' => ['title' => 'Erreurs' , 'description' => 'Message lorsque le formulaire contient des erreurs' , 'default' => 'Il y a eu des erreurs dans votre formulaire'],
|
||||
'messages_partial' => ['title' => 'Utiliser un modèle partiel personnalisé', 'description' => 'Modifier le message flash avec un modèle partiel personnalisé de votre thème'],
|
||||
'mail_enabled' => ['title' => 'Envoyer des notifications' , 'description' => 'Envoyer des notifications par mail sur chaque formulaire envoyé'],
|
||||
'mail_subject' => ['title' => 'Sujet' , 'description' => 'Remplacer le sujet par défaut du courrier électronique'],
|
||||
'mail_recipients' => ['title' => 'Destinataires' , 'description' => 'Spécifier les destinataires des e-mails (ajouter une adresse par ligne)'],
|
||||
'mail_bcc' => ['title' => 'CCI' , 'description' => 'Envoyer une copie carbone invisible aux destinataires des e-mails (ajouter une adresse par ligne)'],
|
||||
'mail_replyto' => ['title' => 'Champ du email de réponse (ReplyTo)' , 'description' => 'Champ de formulaire contenant l\'adresse e-mail de l\'expéditeur à utiliser comme "ReplyTo"'],
|
||||
'mail_uploads' => ['title' => 'Envoyer les téléchargements' , 'description' => 'Envoi des fichiers téléchargés en pièce jointe'],
|
||||
'mail_template' => ['title' => 'Modèle e-mail' , 'description' => 'Utiliser un modèle e-mail personnalisé. Spécifiez un code de modèle tel que "martin.forms::mail.notification" (situé dans Paramètres, Modèles des e-mails). Laissez vide pour utiliser les paramètres par défaut.'],
|
||||
'mail_uploads' => ['title' => 'Envoyer les téléchargements' , 'description' => 'Envoyer les téléchargements en pièces jointes'],
|
||||
'mail_resp_enabled' => ['title' => 'Envoyer une réponse automatique' , 'description' => 'Envoyer un e-mail d\'auto-réponse à la personne qui soumet le formulaire'],
|
||||
'mail_resp_field' => ['title' => 'Champ email' , 'description' => 'Champ de formulaire contenant l\'adresse e-mail du destinataire de réponse automatique '],
|
||||
'mail_resp_from' => ['title' => 'Adresse de l\'expéditeur' , 'description' => 'Adresse e-mail de l\'expéditeur du courrier électronique de réponse automatique (par exemple nepasrepondre@votreentreprise.com)'],
|
||||
'mail_resp_subject' => ['title' => 'Sujet' , 'description' => 'Remplacer le sujet par défaut du courrier électronique'],
|
||||
'reset_form' => ['title' => 'Réinitialiser le formulaire' , 'description' => 'Réinitialiser le formulaire après l\'envoi réussi'],
|
||||
'redirect' => ['title' => 'Redirection envoi réussi' , 'description' => 'Rediriger vers une URL spécifique lors de l\'envoi réussi. Remarque: doit être une URL valide commençant par http ou https ou la redirection sera ignorée.'],
|
||||
'inline_errors' => ['title' => 'Erreurs sur la même ligne' , 'description' => 'Afficher les erreurs sur la même ligne. Cela nécéssite du code supplémentaire, consultez la documentation pour plus d\'informations. ', 'disabled' => 'Désactivé', 'display' => 'Afficher les erreurs', 'variable' => 'Variable Javascript'],
|
||||
'js_on_success' => ['title' => 'JS au succès' , 'description' => 'Exécutez du code JavaScript personnalisé lorsque le formulaire a été soumis avec succès. Ne pas utiliser les balises script'],
|
||||
'js_on_error' => ['title' => 'JS si erreur' , 'description' => 'Exécutez du code JavaScript personnalisé lorsque le formulaire ne se valide pas. Ne pas utiliser les balises script.'],
|
||||
'allowed_fields' => ['title' => 'Allowed Fields' , 'description' => 'Spécifiez quels champs doivent être filtrés et stockés (ajoutez un nom de champ par ligne).'],
|
||||
'anonymize_ip' => ['title' => 'Anonymiser IP' , 'description' => 'Ne pas enregistrer les adresses IP', 'full' => 'Complet', 'partial' => 'Partiel', 'disabled' => 'Désactivé'],
|
||||
'sanitize_data' => ['title' => 'Désinfecter les données de formulaire' , 'description' => 'Désinfectez les données de formulaire et enregistrez le résultat dans la base de données', 'disabled' => 'Désactivé', 'htmlspecialchars' => 'Utilisez htmlspecialchars'],
|
||||
'recaptcha_enabled' => ['title' => 'Activer reCAPTCHA' , 'description' => 'Insère le widget reCAPTCHA sur votre formulaire'],
|
||||
'recaptcha_theme' => ['title' => 'Thème' , 'description' => 'Le thème de couleur du widget' , 'light' => 'Léger' , 'dark' => 'Sombre'],
|
||||
'recaptcha_type' => ['title' => 'Type' , 'description' => 'Le type de CAPTCHA à servir' , 'image' => 'Image' , 'audio' => 'Audio'],
|
||||
'recaptcha_size' => ['title' => 'Taille' , 'description' => 'La taille du widget' , 'normal' => 'Normale', 'compact' => 'Compacte'],
|
||||
'skip_database' => ['title' => 'Ignore la BDD' , 'description' => 'Ne pas stocker ce formulaire dans la base de données. Utile si vous souhaitez utiliser des évènements avec votre plugin personnalisé.'],
|
||||
'uploader_enable' => ['title' => 'Autoriser les téléchargements' , 'description' => 'Activer le téléchargement des fichiers. Vous devez activer cette option explicitement comme mesure de sécurité.'],
|
||||
'uploader_multi' => ['title' => 'Fichiers multiples' , 'description' => 'Autoriser plusieurs téléchargements de fichiers'],
|
||||
'uploader_pholder' => ['title' => 'Texte de remplacement' , 'description' => 'Texte à afficher quand aucun fichier n\'est téléchargé', 'default' => 'Cliquez pour choisir ou faites glisser les fichiers à télécharger'],
|
||||
'uploader_maxsize' => ['title' => 'Limite de taille de fichier' , 'description' => 'Taille maximale du fichier pouvant être téléchargée en mégaoctets'],
|
||||
'uploader_types' => ['title' => 'Types de fichiers autorisés' , 'description' => 'Extensions de fichiers autorisées ou étoile (*) pour tous les types (ajoutez une extension par ligne)'],
|
||||
'uploader_remFile' => ['title' => 'Texte de Suppression' , 'description' => 'Texte à afficher dans la popup lors de la suppression d\'un fichier', 'default' => 'Êtes-vous sûr ?'],
|
||||
]
|
||||
],
|
||||
'settings' => [
|
||||
'tabs' => ['general' => 'Général', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'RGPD'],
|
||||
'section_flash_messages' => 'Messages Flash',
|
||||
'global_messages_success' => ['label' => 'Message de réussite', 'comment' => '(Ce paramètre peut être remplacé par le composant)', 'default' => 'Votre formulaire a été envoyé avec succès'],
|
||||
'global_messages_errors' => ['label' => 'Global Errors Message' , 'comment' => '(Ce paramètre peut être remplacé par le composant)', 'default' => 'Il y a eu des erreurs dans votre formulaire'],
|
||||
'plugin_help' => 'Vous pouvez accéder à la documentation du plugin sur GitHub :',
|
||||
'global_hide_button' => 'Masquer l\'élément de navigation',
|
||||
'global_hide_button_desc' => 'Utile si vous souhaitez utiliser des événements avec votre plugin personnalisé.',
|
||||
'section_recaptcha' => 'Paramètres reCAPTCHA',
|
||||
'recaptcha_site_key' => 'Clé du site',
|
||||
'recaptcha_secret_key' => 'Clé secrète',
|
||||
'gdpr_help_title' => 'Information',
|
||||
'gdpr_help_comment' => 'Nouvelle loi RGPD/GDPR en Europe : vous ne pouvez pas conserver les enregistrements indéfiniment, vous devez les effacer au bout d\'un certain temps en fonction de vos besoins.',
|
||||
'gdpr_enable' => 'Activer RGPD',
|
||||
'gdpr_days' => 'Garder les enregistrements pour un maximum de X jours',
|
||||
],
|
||||
'permissions' => [
|
||||
'tab' => 'Magic Forms',
|
||||
'access_records' => 'Accéder aux données des formulaires stockés',
|
||||
'access_exports' => 'Accès aux exports des formulaires stockés',
|
||||
'access_settings' => 'Accès à la configuration du module',
|
||||
'gdpr_cleanup' => 'Accès au nettoyage de la base de donnée RGPD',
|
||||
],
|
||||
'mails' => [
|
||||
'form_notification' => ['description' => 'Notifier quand un formulaire est envoyé'],
|
||||
'form_autoresponse' => ['description' => 'Auto-Réponse lorsqu\'un formulaire est envoyé'],
|
||||
],
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'Impossible de valider le champ reCAPTCHA'
|
||||
],
|
||||
'classes' => [
|
||||
'GDPR' => [
|
||||
'alert_gdpr_disabled' => 'LEs options RGPD sont désactivés',
|
||||
'alert_invalid_gdpr' => 'Valeur de réglage des jours RGPD invalide',
|
||||
]
|
||||
]
|
||||
];
|
||||
?>
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'plugin' => [
|
||||
'name' => 'Magic Forms',
|
||||
'description' => 'Crie formulários fácilmente com AJAX'
|
||||
],
|
||||
'menu' => [
|
||||
'label' => 'Formulários Mágico',
|
||||
'records' => ['label' => 'Registros'],
|
||||
'exports' => ['label' => 'Exportar'],
|
||||
'settings' => 'Configurar parâmetros do plug-in',
|
||||
],
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => 'Ver registos',
|
||||
'view_title' => 'Detalhes do registro',
|
||||
'error' => 'Registro não encontrado',
|
||||
'deleted' => 'Registro excluído com sucesso',
|
||||
'columns' => [
|
||||
'id' => 'ID do registro',
|
||||
'group' => 'Grupo',
|
||||
'ip' => 'Endereço IP',
|
||||
'form_data' => 'Campos armazenados',
|
||||
'files' => 'Arquivos anexados',
|
||||
'created_at' => 'Criado',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Exportar registros',
|
||||
'breadcrumb' => 'Exportar',
|
||||
'filter_section' => '1. Filtrar registos',
|
||||
'filter_type' => 'Exportar todos os registros',
|
||||
'filter_groups' => 'Grupos',
|
||||
'filter_date_after' => 'Data após',
|
||||
'filter_date_before' => 'Data anterior',
|
||||
'options_section' => '2. Opções extras',
|
||||
'options_metadata' => 'Incluir metadados',
|
||||
'options_metadata_com' => 'Exportar registros com metadados (ID de registro, grupo, IP, data criação)',
|
||||
'options_deleted' => 'Incluir registros excluídos',
|
||||
],
|
||||
],
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => 'Formulário AJAX Genérico',
|
||||
'description' => 'Por padrão renderiza um formulário genérico; Substitua o componente HTML com seus campos personalizados.',
|
||||
],
|
||||
'upload_form' => [
|
||||
'name' => 'formulário upload AJAX [BETA]',
|
||||
'description' => 'Mostra como implementar uploads de arquivos no formulário.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => 'Formulário AJAX vazio',
|
||||
'description' => 'Crie um modelo vazio para seu formulário personalizado; Substituir o componente HTML.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'A sessão do formulário expirou! Atualize a página.',
|
||||
'recaptcha_warn' => 'Aviso: reCAPTCHA não está configurado corretamente. Por favor, vá para Back-end> Configurações> CMS> Magic Forms e configure.',
|
||||
'group_validation' => 'Validação de formulários',
|
||||
'group_messages' => 'Mensagens Flash',
|
||||
'group_mail' => 'Configurações de Notificações',
|
||||
'group_mail_resp' => 'Configurações de Resposta Automática',
|
||||
'group_settings' => 'Mais configurações',
|
||||
'group_security' => 'Segurança',
|
||||
'group_recaptcha' => 'Configurações de reCAPTCHA',
|
||||
'group_uploader' => 'Configurações do Uploader',
|
||||
'validation_req' => 'A propriedade é obrigatória',
|
||||
'group' => ['title' => 'Grupo' , 'description' => 'Organize seus formulários com um nome de grupo personalizado. Esta opção é útil ao exportar dados.'],
|
||||
'rules' => ['title' => 'Regras' , 'description' => 'Defina suas próprias regras usando a validação Laravel'],
|
||||
'rules_messages' => ['title' => 'Mensagens de Regras' , 'description' => 'Use suas próprias mensagens de regras usando a validação do Laravel'],
|
||||
'messages_success' => ['title' => 'Sucesso' , 'description' => 'Mensagem quando o formulário é enviado com sucesso', 'default '=>' Seu formulário foi enviado com sucesso'],
|
||||
'messages_errors' => ['title' => 'Erros' , 'description' => 'Mensagem quando o formulário contém erros', 'default' => 'Ocorreu um erro no envio'],
|
||||
'mail_enabled' => ['title' => 'Enviar Notificações' , 'description' => 'Enviar notificações por email em todos os formulários enviados'],
|
||||
'mail_subject' => ['title' => 'Assunto' , 'description' => 'Substitui o assunto de email padrão'],
|
||||
'mail_recipients' => ['title' => 'Destinatários' , 'description' => 'Especifique destinatários de email (adicione um endereço por linha)'],
|
||||
'mail_uploads' => ['title' => 'Enviar Uploads' , 'description' => 'Enviar uploads como anexos'],
|
||||
'mail_resp_enabled' => ['title' => 'Enviar Resposta Automática' , 'description' => 'Enviar um email de resposta automática para a pessoa que envia o formulário'],
|
||||
'mail_resp_field' => ['title' => 'Campo de Email' , 'description' => 'Campo de formulário que contém o endereço de email do destinatário da resposta automática'],
|
||||
'mail_resp_from' => ['title' => 'Endereço do Remetente' , 'description' => 'Endereço de email do remetente de e-mail de resposta automática (por exemplo, noreply@yourcompany.com)'],
|
||||
'mail_resp_subject' => ['title' => 'Assunto' , 'description' => 'Substitui assunto de e-mail padrão'],
|
||||
'reset_form' => ['title' => 'Limpar Fomulário' , 'description' => 'Limpar o formulário após o envio com sucesso'],
|
||||
'allowed_fields' => ['title' => 'Campos permitidos' , 'description' => 'Especifique quais campos devem ser filtrados e armazenados (adicione um nome de campo por linha)'],
|
||||
'recaptcha_enabled' => ['title' => 'Habiltar reCAPTCHA' , 'description' => 'Inserir o widget reCAPTCHA no seu formulário'],
|
||||
'recaptcha_theme' => ['title' => 'Tema' , 'description' => 'Cor do tema do widget', 'light' => 'Claro' , 'dark' => 'Escuro'],
|
||||
'recaptcha_type' => ['title' => 'Tipo' , 'description' => 'O tipo de CAPTCHA para servir' , 'image' => 'Imagem' , 'audio' => 'Áudio'],
|
||||
'recaptcha_size' => ['title' => 'Tamanho' , 'description' => 'O tamanho do widget' , 'normal' => 'Normal', 'compact' => 'Compacto'],
|
||||
'uploader_enable' => ['title' => 'Permitir Uploads' , 'description' => 'Ativar o upload de arquivos. Você precisa ativar essa opção explicitamente como uma medida de segurança.'],
|
||||
'uploader_multi' => ['title' => 'Multiplos Arquivos' , 'description' => 'Permitir multiplos uploads de arquivos'],
|
||||
'uploader_pholder' => ['title' => 'texto do Placeholder' , 'description' => 'Texto a ser exibido quando nenhum arquivo é carregado', 'default' => 'Clique ou arraste os arquivos para fazer o upload'],
|
||||
'uploader_maxsize' => ['title' => 'Limite do Tamanho do Arquivo' , 'description' => 'O tamanho máximo de arquivo que pode ser carregado em megabytes'],
|
||||
'uploader_types' => ['title' => 'Tipos de Arquivos Permitidos' , 'description' => 'Extensões de arquivo permitido ou asterisco (*) para todos os tipos (adicionar uma extensão por linha)'],
|
||||
]
|
||||
],
|
||||
'settings' => [
|
||||
'section_flash_messages' => 'Mensagens Flash',
|
||||
'global_messages_success' => ['label' => 'Mensagem de sucesso global' , 'comment' => '(Esta definição pode ser substituída a partir do componente)', 'default' => 'Seu formulário foi enviado com sucesso'],
|
||||
'global_messages_errors' => ['label' => 'Mensagem de Erros Global' , 'comment' => '(Esta definição pode ser substituída a partir do componente)', 'default' => 'Ocorreu um erro o envio do formulário'],
|
||||
'section_recaptcha' => 'Configurações de reCAPTCHA',
|
||||
'recaptcha_site_key' => 'Chave do site',
|
||||
'recaptcha_secret_key' => 'Chave secreta',
|
||||
],
|
||||
'permissions' => [
|
||||
'tab' => 'Formulários Mágico',
|
||||
'access_records' => 'Acessar dados de formulários armazenados',
|
||||
'access_exports' => 'Acesso à exportação de dados armazenados',
|
||||
'access_settings' => 'Acesso do módulo de configuração',
|
||||
],
|
||||
'mails' => [
|
||||
'form_notification' => ['description' => 'Notificar quando um formulário é enviado'],
|
||||
'form_autoresponse' => ['description' => 'Resposta automática quando um formulário é enviado'],
|
||||
],
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'Não é possível validar o campo reCAPTCHA'
|
||||
],
|
||||
];
|
||||
?>
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'plugin' => [
|
||||
'name' => 'Magic Forms',
|
||||
'description' => 'С легкостью создайте AJAX форму'
|
||||
],
|
||||
|
||||
'menu' => [
|
||||
'label' => 'Magic Forms',
|
||||
'records' => ['label' => 'Записи'],
|
||||
'exports' => ['label' => 'Экспорт'],
|
||||
'settings' => 'Настройки плагина',
|
||||
],
|
||||
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => 'Все заявки',
|
||||
'view_title' => 'Запись',
|
||||
'error' => 'Запись не найдена',
|
||||
'deleted' => 'Запись успешно удалена',
|
||||
'columns' => [
|
||||
'id' => 'ID записи',
|
||||
'group' => 'Группа',
|
||||
'ip' => 'IP адрес',
|
||||
'form_data' => 'Поля формы',
|
||||
'files' => 'Прикрепленные файлы',
|
||||
'created_at' => 'Создано',
|
||||
],
|
||||
'buttons' => [
|
||||
'read' => 'Отметить как прочитано',
|
||||
'unread' => 'Отметить как непрочитанное',
|
||||
'gdpr_clean' => 'Очистить GDPR',
|
||||
],
|
||||
'alerts' => [
|
||||
'gdpr_confirm' => "Вы действительно хотите удалить эти старые записи?\nЭто действие не может быть отменено!",
|
||||
'gdpr_success' => 'Очистка GDPR успешна',
|
||||
'gdpr_perms' => 'У вас нет прав использования этой функции',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Экспорт записей',
|
||||
'breadcrumb' => 'Экспорт',
|
||||
'filter_section' => '1. Фильтровать записи',
|
||||
'filter_type' => 'Экспорт всех записей',
|
||||
'filter_groups' => 'Группы',
|
||||
'filter_date_after' => 'Дата от',
|
||||
'filter_date_before' => 'дата до',
|
||||
'options_section' => '2. Экстра опции',
|
||||
'options_metadata' => 'Вложить метаданные',
|
||||
'options_metadata_com' => 'Экспорт записей с метаданными(ID, группа, дата создания)',
|
||||
'options_deleted' => 'Вложить удаленные записи',
|
||||
'options_delimiter' => 'Использовать альтернативный разделитель полей',
|
||||
'options_delimiter_com' => 'Точка с запятой вместо запятой',
|
||||
'options_utf' => 'Кодировать в UTF8',
|
||||
'options_utf_com' => 'Ваш CSV файл будет закодирван в UTF-8',
|
||||
],
|
||||
],
|
||||
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => 'Стандартная AJAX форма',
|
||||
'description' => 'Выводит стандартную форму; Перезапишите HTML компонент со своей формой.',
|
||||
],
|
||||
'upload_form' => [
|
||||
'name' => 'AJAX форма с файлами',
|
||||
'description' => '[BETA] Форма с возможностью прикрепления файлов.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => 'Пустая AJAX форма',
|
||||
'description' => 'Создает пустой плейсхолдер для вашей формы, который вы должны перезаписать своим HTML кодом.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'Сессия формы истекла! Перезагрузите страницу.',
|
||||
'recaptcha_warn' => 'Внимание: reCAPTCHA настроена не правильно. Пожалуйста, перейдите в Настройки > CMS > Magic Forms и заполните все обязательные поля.',
|
||||
'group_validation' => 'Валидация формы',
|
||||
'group_messages' => 'Флэш-сообщения',
|
||||
'group_mail' => 'Настройки уведомлений',
|
||||
'group_mail_resp' => 'Настройки авто-ответа',
|
||||
'group_settings' => 'Остальные настройки',
|
||||
'group_security' => 'Безопасность',
|
||||
'group_recaptcha' => 'Настройки reCAPTCHA',
|
||||
'group_advanced' => 'Продвинутые настройки',
|
||||
'group_uploader' => 'Настройки файлового загрузчика',
|
||||
'validation_req' => 'Этот атрибут обязателен',
|
||||
'group' => ['title' => 'Группа' , 'description' => 'Организуйте свои формы по группам. Эта опция полезна для экспорта записей.'],
|
||||
'rules' => ['title' => 'Правила' , 'description' => 'Используйте валидацию Laravel для установки своих правил обработки полей.'],
|
||||
'rules_messages' => ['title' => 'Сообщения от правил', 'description' => 'Используйте свои собственные сообщения от кастомных валидаций.'],
|
||||
'custom_attributes' => ['title' => 'Кастомные атрибуты' , 'description' => 'Используйте кастомные атрибуты в своих правилах валидаций.'],
|
||||
'messages_success' => ['title' => 'Успешно' , 'description' => 'Сообщение об успешной отправке формы', 'default' => 'Ваша форма была успешно отправлена!'],
|
||||
'messages_errors' => ['title' => 'Ошибки' , 'description' => 'Сообщение с ошибками формы' , 'default' => 'В вашей заявке содержатся ошибки.'],
|
||||
'messages_partial' => ['title' => 'Кастомный фрагмент' , 'description' => 'Переопределить стандартные уведомления кастомным фрагментом из вашей фронтенд темы.'],
|
||||
'mail_enabled' => ['title' => 'Отправка уведомлений','description' => 'Отправлять увдеомления по email после каждого успешного заполенния формы.'],
|
||||
'mail_subject' => ['title' => 'Тема письма' , 'description' => 'Переопределить тему письма по умолчанию'],
|
||||
'mail_recipients' => ['title' => 'Получатели' , 'description' => 'Укажите получателей электронной почты (По одному адресу в строке)'],
|
||||
'mail_bcc' => ['title' => 'BCC' , 'description' => 'Отправить копию получателям электронной почты (По одному адресу в строке)'],
|
||||
'mail_replyto' => ['title' => 'ReplyTo поле email' , 'description' => 'Поле формы, содержащее адрес электронной почты отправителя, который будет использоваться как "ReplyTo"'],
|
||||
'mail_template' => ['title' => 'Шаблон письма' , 'description' => 'Используйте собственный шаблон письма. Укажите код шаблона, например, "martin.forms::mail.notification" (находится в разделе Настройки -> Почтовые шаблоны). Оставьте пустым, чтобы использовать по умолчанию.'],
|
||||
'mail_uploads' => ['title' => 'Прикреплять файлы' , 'description' => 'Прикреплять файлы к письму'],
|
||||
'mail_resp_enabled' => ['title' => 'Отправить авто-ответ','description' => 'Отправить автоответчик на email отправителя.'],
|
||||
'mail_resp_field' => ['title' => 'Email поле' , 'description' => 'Имя поля с email отправителя'],
|
||||
'mail_resp_from' => ['title' => 'Email адрес отправителя', 'description' => 'Адрес электронной почты отправителя электронной почты с автоматическим ответом (например, noreply@yourcompany.com)'],
|
||||
'mail_resp_subject' => ['title' => 'Тема пиьсма' , 'description' => 'Переопределить тему письма по умолчанию'],
|
||||
'reset_form' => ['title' => 'Сбросить форму' , 'description' => 'Сбросить форму после успешной отправки'],
|
||||
'redirect' => ['title' => 'Редирект' , 'description' => 'Редирект на URL после успешной отправки формы.'],
|
||||
'inline_errors' => ['title' => 'Встроенные ошибки' , 'description' => 'Отображать встроенные ошибки. Это требует дополнительного кода, проверьте документацию для получения дополнительной информации.', 'disabled' => 'Отключено', 'display' => 'Отображать ошибки', 'variable' => 'JS переменная'],
|
||||
'js_on_success' => ['title' => '"Успешный" JS' , 'description' => 'Выполнияет скрипт после успешной отправки формы. Не используйте <script> теги.'],
|
||||
'js_on_error' => ['title' => '"Ошибочный" JS' , 'description' => 'Выполнияет скрипт после не успешной отправки формы. Не используйте <script> теги'],
|
||||
'allowed_fields' => ['title' => 'Разрешенные поля' , 'description' => 'Укажите, какие поля должны быть отфильтрованы и сохранены (По одному адресу в строке)'],
|
||||
'anonymize_ip' => ['title' => 'Анонимный IP' , 'description' => 'Не хранить IP-адрес', 'full' => 'Полный', 'partial' => 'Частичынй', 'disabled' => 'Отключено'],
|
||||
'sanitize_data' => ['title' => '"Sanitize" данные формы' , 'description' => 'Очищает поля формы от возмонжой XSS атаки', 'disabled' => 'Отключено', 'htmlspecialchars' => 'Использовать htmlspecialchars'],
|
||||
'recaptcha_enabled' => ['title' => 'Включить reCAPTCHA' , 'description' => 'Внедряет reCAPTCHA виджет в вашу форму (не забудьте добавить фрагмент).'],
|
||||
'recaptcha_theme' => ['title' => 'Тема оформления' , 'description' => 'Цветовая тема виджета', 'light' => 'Светлая' , 'dark' => 'Темная'],
|
||||
'recaptcha_type' => ['title' => 'Тип' , 'description' => 'Тип CAPTCHA', 'image' => 'Картинки' , 'audio' => 'Аудио'],
|
||||
'recaptcha_size' => ['title' => 'Размер' , 'description' => 'Размер виджета', 'normal' => 'Обычный', 'compact' => 'Комактный'],
|
||||
'skip_database' => ['title' => 'Пропустить Базу' , 'description' => 'Не хранить записи в базе данных. Полезно, если вы хотите использовать события с вашим плагином.'],
|
||||
'uploader_enable' => ['title' => 'Разрешить загрузку' , 'description' => 'Включить загрузку файлов. Разрешает форме принимать файлы.'],
|
||||
'uploader_multi' => ['title' => 'Множественные файлы', 'description' => 'Разрешить загрузку множества файлов (Если выключено то форма сможет принять только один файл)'],
|
||||
'uploader_pholder' => ['title' => 'Placeholder текст' , 'description' => 'Формулировка, отображаемая при отсутствии загруженного файла.', 'default' => 'Нажмите или перетащите файлы для загрузки'],
|
||||
'uploader_maxsize' => ['title' => 'Ограничение размера файла', 'description' => 'Максимальный размер файла, который может быть загружен в мегабайтах'],
|
||||
'uploader_types' => ['title' => 'Разрешенные типы файлов', 'description' => 'Разрешенные расширения файлов или звездочка (*) для всех типов (По одному расширению в строке)'],
|
||||
'uploader_remFile' => ['title' => 'Popup текст об удалении' , 'description' => 'Текст для отображения во всплывающем окне при удалении файла', 'default' => 'Вы уверены?'],
|
||||
]
|
||||
],
|
||||
|
||||
'settings' => [
|
||||
'tabs' => ['general' => 'Основное', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'GDPR'],
|
||||
'section_flash_messages' => 'Флэш-сообщения',
|
||||
'global_messages_success' => ['label' => 'Глобальное сообщение об успешной отправки формы', 'comment' => '(Этот параметр может быть переопределен из компонента)', 'default' => 'Ваша форма была успешно отправлена!'],
|
||||
'global_messages_errors' => ['label' => 'Глобальное сообщение об не успешной отправки формы' , 'comment' => '(Этот параметр может быть переопределен из компонента)', 'default' => 'В вашей заявке содержатся ошибки.'],
|
||||
'plugin_help' => 'Вы можете получить доступ к документации по плагину в GitHub:',
|
||||
'global_hide_button' => 'Скрыть элемент навигации',
|
||||
'global_hide_button_desc' => 'Полезно, если вы хотите использовать события с вашим плагином.',
|
||||
'section_recaptcha' => 'reCAPTCHA настройки',
|
||||
'recaptcha_site_key' => 'Site key',
|
||||
'recaptcha_secret_key' => 'Secret key',
|
||||
'gdpr_help_title' => 'Информация',
|
||||
'gdpr_help_comment' => 'Новый закон о GDPR в Европе, вы не можете хранить записи бесконечно, необходимо очистить их через определенный промежуток времени в зависимости от ваших потребностей',
|
||||
'gdpr_enable' => 'Включить GDPR',
|
||||
'gdpr_days' => 'Хранение записей максимум Х дней',
|
||||
],
|
||||
|
||||
'permissions' => [
|
||||
'tab' => 'Magic Forms',
|
||||
'access_records' => 'Доступ к записям форм',
|
||||
'access_exports' => 'Доступ к экспорту записей',
|
||||
'access_settings' => 'Доступ к настройкам модуля',
|
||||
'gdpr_cleanup' => 'Разрешить GDPR очистку',
|
||||
],
|
||||
|
||||
'mails' => [
|
||||
'form_notification' => ['description' => 'Уведомлять когда форма отправлена успешно'],
|
||||
'form_autoresponse' => ['description' => 'Авто-ответ при успешной отправке формы'],
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'Не удается проверить поле reCAPTCHA'
|
||||
],
|
||||
|
||||
'classes' => [
|
||||
'GDPR' => [
|
||||
'alert_gdpr_disabled' => 'GDPR опции отключены',
|
||||
'alert_invalid_gdpr' => 'Некорретное занчение дней в настройках GDPR',
|
||||
]
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'plugin' => [
|
||||
'name' => 'Magic Forms',
|
||||
'description' => 'Kolay AJAX formları oluşturun'
|
||||
],
|
||||
|
||||
'menu' => [
|
||||
'label' => 'Sihirli Formlar',
|
||||
'records' => ['label' => 'Kayıtlar'],
|
||||
'exports' => ['label' => 'Dışa Aktar'],
|
||||
'settings' => 'Eklenti parametrelerini yapılandır',
|
||||
],
|
||||
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => 'Kayıtları Görüntüle',
|
||||
'view_title' => 'Kayıt Ayrıntıları',
|
||||
'error' => 'Kayıt bulunamadı',
|
||||
'deleted' => 'Kayıt başarıyla silindi',
|
||||
'columns' => [
|
||||
'id' => 'Kayıt Kimliği',
|
||||
'group' => 'Grup',
|
||||
'ip' => 'IP Adresi',
|
||||
'form_data' => 'Kayıtlı Alanlar',
|
||||
'created_at' => 'Oluşturuldu',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Dışa Aktarma Kayıtları',
|
||||
'breadcrumb' => 'Dışa Aktarma',
|
||||
'filter_section' => '1. Filtre kayıtları',
|
||||
'filter_type' => 'Tüm kayıtları dışa aktar',
|
||||
'filter_groups' => 'Gruplar',
|
||||
'filter_date_after' => 'Tarihinden sonra',
|
||||
'filter_date_before' => 'Tarihinden önce',
|
||||
'options_section' => '2. Ekstra seçenekler',
|
||||
'options_metadata' => 'Meta verileri içe aktar',
|
||||
'options_metadata_com' => 'Meta verilerle kayıtları dışa aktarmak (Kayıt Kimliği, grup, IP, oluşturma tarihi)',
|
||||
'options_deleted' => 'Silinmiş kayıtları dahil et',
|
||||
],
|
||||
],
|
||||
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => 'Genel AJAX Formu',
|
||||
'description' => 'Varsayılan olarak, genel bir form oluşturur; Özel alanlarınızla, HTML bileşenlerini geçersiz kılar.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => 'Boş AJAX Formu',
|
||||
'description' => 'Özel formunuz için boş bir şablon oluşturun; HTML bileşenlerini geçersiz kıl.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'Form oturumu doldu! Lütfen sayfayı yenileyin.',
|
||||
'recaptcha_warn' => 'Uyarı: reCAPTCHA düzgün şekilde yapılandırılmadı',
|
||||
'group_validation' => 'Form Doğrulaması',
|
||||
'group_messages' => 'Flash Mesajları',
|
||||
'group_security' => 'Güvenlik',
|
||||
'group_mail' => 'Posta Ayarları',
|
||||
'group_recaptcha' => 'ReCAPTCHA Ayarları',
|
||||
'validation_req' => 'Özellikler gereklidir',
|
||||
'group' => ['title' => 'Grup' , 'description' => 'Formlarınızı özel bir grup adı ile düzenleyin. Bu seçenek, veri aktarırken yararlıdır.'],
|
||||
'rules' => ['title' => 'Kurallar' , 'description' => 'Laravel doğrulamasını kullanarak kendi kurallarınızı belirleyin'],
|
||||
'rules_messages' => ['title' => 'Kural Mesajları' , 'description' => 'Laravel doğrulamasını kullanarak kendi kural mesajlarını kullanın'],
|
||||
'messages_success' => ['title' => 'Başarı' , 'description' => 'Form başarıyla gönderildiğinde gönderilecek ileti', 'default' => 'Formunuz başarıyla gönderildi' ],
|
||||
'messages_errors' => ['title' => 'Hatalar' , 'description' => 'Formda hatalar olduğunda gösterilecek mesaj' , 'default' => 'Gönderiminizle ilgili hatalar vardı.'],
|
||||
'allowed_fields' => ['title' => 'İzin Verilen Alanlar', 'description' => 'Filtrelenecek ve depolanacak alanları belirtin (her satır için bir alan adı ekleyin)'],
|
||||
'mail_enabled' => ['title' => 'Posta Bildirimleri' , 'description' => 'Gönderilen her formda posta gönder'],
|
||||
'mail_recipients' => ['title' => 'Posta Alıcıları' , 'description' => 'E-posta alıcılarını belirtin (her hat için bir adres ekleyin)'],
|
||||
'recaptcha_enabled' => ['title' => 'ReCAPTCHAyı etkinleştir' , 'description' => 'ReCAPTCHA widgetını formunuza ekleyin.'],
|
||||
'recaptcha_theme' => ['title' => 'Tema' , 'description' => 'Widgetin renk teması', 'light' => 'Aydınlık' , 'dark' => 'Karanlık'],
|
||||
'recaptcha_type' => ['title' => 'Tip' , 'description' => 'Gösterilecek CAPTCHA türü' , 'image' => 'Resim' , 'audio' => 'Ses'],
|
||||
'recaptcha_size' => ['title' => 'Boyut' , 'description' => 'Widgetın boyutu' , 'normal' => 'Normal', 'compact' => 'Kompakt'],
|
||||
]
|
||||
],
|
||||
|
||||
'settings' => [
|
||||
'section_flash_messages' => 'Flash Mesajları',
|
||||
'global_messages_success' => ['label' => 'Genel Başarı Mesajları', 'comment' => '(Bu ayar bileşenden geçersiz kılınabilir)', 'default' => 'Formunuz başarıyla gönderildi'],
|
||||
'global_messages_errors' => ['label' => 'Genel Hata Mesajları' , 'comment' => '(Bu ayar bileşenden geçersiz kılınabilir)', 'default' => 'Gönderiminizle ilgili hatalar vardı.'],
|
||||
'section_recaptcha' => 'ReCAPTCHA Ayarları',
|
||||
'recaptcha_site_key' => 'Site anahtarı',
|
||||
'recaptcha_secret_key' => 'Gizli anahtar',
|
||||
],
|
||||
|
||||
'permissions' => [
|
||||
'tab' => 'Sihirli Formlar',
|
||||
'access_records' => 'Kayıtlı form verilerine erişme',
|
||||
'access_exports' => 'Saklanan verilere dışa aktarma erişimi',
|
||||
'access_settings' => 'Erişim modülü yapılandırması',
|
||||
],
|
||||
|
||||
'mails' => [
|
||||
'form_notification' => [
|
||||
'description' => 'Bir form gönderildiğinde bildirimde bulunun'
|
||||
]
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'ReCAPTCHA alanını doğrulayamıyorum'
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'plugin' => [
|
||||
'name' => '表单',
|
||||
'description' => '管理网站所有提交表单内容'
|
||||
],
|
||||
|
||||
'menu' => [
|
||||
'label' => '表单',
|
||||
'records' => ['label' => '记录'],
|
||||
'exports' => ['label' => '导出'],
|
||||
'settings' => '设置插件参数',
|
||||
],
|
||||
|
||||
'controllers' => [
|
||||
'records' => [
|
||||
'title' => '查看记录',
|
||||
'view_title' => '记录详情',
|
||||
'error' => '未找到该记录',
|
||||
'deleted' => '记录删除成功',
|
||||
'columns' => [
|
||||
'id' => '记录 ID',
|
||||
'group' => '组',
|
||||
'ip' => 'IP 地址',
|
||||
'form_data' => '记录字段',
|
||||
'files' => '附件',
|
||||
'created_at' => '创建于',
|
||||
],
|
||||
'buttons' => [
|
||||
'read' => '标记为已读',
|
||||
'unread' => '标记为未读',
|
||||
'gdpr_clean' => 'GDPR 清理',
|
||||
],
|
||||
'alerts' => [
|
||||
'gdpr_confirm' => '你确定需要清除旧数据吗?\n此操作无法撤销!',
|
||||
'gdpr_success' => 'GDPR 清理执行成功',
|
||||
'gdpr_perms' => '没有该权限',
|
||||
],
|
||||
],
|
||||
'exports' => [
|
||||
'title' => '导出记录',
|
||||
'breadcrumb' => '导出',
|
||||
'filter_section' => '1. 过滤记录',
|
||||
'filter_type' => '导出所有记录',
|
||||
'filter_groups' => '组',
|
||||
'filter_date_after' => '开始日期',
|
||||
'filter_date_before' => '结束日期',
|
||||
'options_section' => '2. 附加选项',
|
||||
'options_metadata' => '包含元数据',
|
||||
'options_metadata_com' => '导出包含元数据的记录(记录 ID,组,IP,创建时间)',
|
||||
'options_deleted' => '包含已删除的记录',
|
||||
],
|
||||
],
|
||||
|
||||
'components' => [
|
||||
'generic_form' => [
|
||||
'name' => '生成 AJAX 表单',
|
||||
'description' => '默认会创建一个常规表单;你可以使用自定义字段来覆盖组件 HTML。',
|
||||
],
|
||||
'upload_form' => [
|
||||
'name' => 'AJAX 上传表单 [BETA]',
|
||||
'description' => 'Shows how to implement file uploads on your form.',
|
||||
],
|
||||
'empty_form' => [
|
||||
'name' => '空 AJAX 表单',
|
||||
'description' => 'Create a empty template for your custom form; override component HTML.',
|
||||
],
|
||||
'shared' => [
|
||||
'csrf_error' => 'Form session expired! Please refresh the page.',
|
||||
'recaptcha_warn' => '警告:reCAPTCHA 配置不正确。请到 后台 > 设置 > CMS > 表单 进行配置。',
|
||||
'group_validation' => '表单验证',
|
||||
'group_messages' => '闪现消息',
|
||||
'group_mail' => '通知设置',
|
||||
'group_mail_resp' => '自动回复设置',
|
||||
'group_settings' => '更多设置',
|
||||
'group_security' => '安全',
|
||||
'group_recaptcha' => 'reCAPTCHA 设置',
|
||||
'group_advanced' => '高级设置',
|
||||
'group_uploader' => '上传设置',
|
||||
'validation_req' => '该属性为必填项',
|
||||
'group' => ['title' => '组' , 'description' => 'Organize your forms with a custom group name. This option is useful when exporting data.'],
|
||||
'rules' => ['title' => '条件' , 'description' => 'Set your own rules using Laravel validation'],
|
||||
'rules_messages' => ['title' => '条件消息' , 'description' => 'Use your own rules messages using Laravel validation'],
|
||||
'custom_attributes' => ['title' => '自定义属性' , 'description' => 'Use your own custom attributes using Laravel validation'],
|
||||
'messages_success' => ['title' => '成功' , 'description' => 'Message when the form is successfully submited', 'default' => 'Your form was successfully submitted' ],
|
||||
'messages_errors' => ['title' => '错误' , 'description' => 'Message when the form contains errors' , 'default' => 'There were errors with your submission'],
|
||||
'messages_partial' => ['title' => '使用自定义 Partial' , 'description' => 'Override flash messages with your custom partial inside your theme'],
|
||||
'mail_enabled' => ['title' => '发送通知' , 'description' => 'Send mail notifications on every form submited'],
|
||||
'mail_subject' => ['title' => '主题' , 'description' => 'Override default email subject'],
|
||||
'mail_recipients' => ['title' => '收件人' , 'description' => 'Specify email recipients (add one address per line)'],
|
||||
'mail_bcc' => ['title' => '密送' , 'description' => 'Send blind carbon copy to email recipients (add one address per line)'],
|
||||
'mail_replyto' => ['title' => '回复邮件字段', 'description' => 'Form field containing the email address of sender to be used as "ReplyTo"'],
|
||||
'mail_template' => ['title' => '邮件模板' , 'description' => 'Use custom mail template. Specify template code like "martin.forms::mail.notification" (found on Settings, Mail templates). Leave empty to use default.'],
|
||||
'mail_uploads' => ['title' => '发送上传文件' , 'description' => 'Send uploads as attachements'],
|
||||
'mail_resp_enabled' => ['title' => '发送自动回复' , 'description' => 'Send an auto-response email to the person submitting the form'],
|
||||
'mail_resp_field' => ['title' => '邮件字段' , 'description' => 'Form field containing the email address of the recipient of auto-response'],
|
||||
'mail_resp_from' => ['title' => '发件人地址' , 'description' => 'Email address of auto-response email sender (e.g. noreply@yourcompany.com)'],
|
||||
'mail_resp_subject' => ['title' => '主题' , 'description' => 'Override default email subject'],
|
||||
'reset_form' => ['title' => '重置表单' , 'description' => 'Reset form after successfully submit'],
|
||||
'redirect' => ['title' => '成功后重定向', 'description' => 'Redirect to URL on successfully submit.'],
|
||||
'inline_errors' => ['title' => '内联错误' , 'description' => 'Display inline errors. This requires extra code, check documentation for more info.', 'disabled' => 'Disabled', 'display' => 'Display errors', 'variable' => 'JS variable'],
|
||||
'js_on_success' => ['title' => '成功回调 JS' , 'description' => 'Execute custom JavaScript code when the form was successfully submitted. Don\'t use script tags.'],
|
||||
'js_on_error' => ['title' => '错误回调 JS' , 'description' => 'Execute custom JavaScript code when the form doesn\'t validate. Don\'t use script tags.'],
|
||||
'allowed_fields' => ['title' => '允许的字段' , 'description' => 'Specify which fields should be filtered and stored (add one field name per line)'],
|
||||
'anonymize_ip' => ['title' => '匿名 IP' , 'description' => 'Don\'t store IP address', 'full' => 'Full', 'partial' => 'Partial', 'disabled' => 'Disabled'],
|
||||
'sanitize_data' => ['title' => '清理表单数据' , 'description' => 'Sanitize form data and save result on database', 'disabled' => 'Disabled', 'htmlspecialchars' => 'Use htmlspecialchars'],
|
||||
'recaptcha_enabled' => ['title' => '启用 reCAPTCHA' , 'description' => 'Insert the reCAPTCHA widget on your form'],
|
||||
'recaptcha_theme' => ['title' => '主题' , 'description' => 'The color theme of the widget', 'light' => 'Light' , 'dark' => 'Dark'],
|
||||
'recaptcha_type' => ['title' => '类型' , 'description' => 'The type of CAPTCHA to serve' , 'image' => 'Image' , 'audio' => 'Audio'],
|
||||
'recaptcha_size' => ['title' => '尺寸' , 'description' => 'The size of the widget' , 'normal' => 'Normal', 'compact' => 'Compact'],
|
||||
'skip_database' => ['title' => '跳过数据库' , 'description' => 'Don\'t store this form on database. Useful if you want to use events with your custom plugin.'],
|
||||
'uploader_enable' => ['title' => '允许上传' , 'description' => 'Enable files uploading. You need to explicitly enable this option as a security measure.'],
|
||||
'uploader_multi' => ['title' => '多文件' , 'description' => 'Allow multipe files uploads'],
|
||||
'uploader_pholder' => ['title' => '占位文字' , 'description' => 'Wording to display when no file is uploaded', 'default' => 'Click or drag files to upload'],
|
||||
'uploader_maxsize' => ['title' => '文件大小限制' , 'description' => 'The maximum file size that can be uploaded in megabytes'],
|
||||
'uploader_types' => ['title' => '允许的文件类型' , 'description' => 'Allowed file extensions or star (*) for all types (add one extension per line)'],
|
||||
'uploader_remFile' => ['title' => '移除时的弹出文字' , 'description' => 'Wording to display in the popup when you remove file', 'default' => 'Are you sure ?'],
|
||||
]
|
||||
],
|
||||
|
||||
'settings' => [
|
||||
'tabs' => ['general' => '常规', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'GDPR'],
|
||||
'section_flash_messages' => '闪现信息',
|
||||
'global_messages_success' => ['label' => '全局成功消息', 'comment' => '(此项设置可以被组件覆盖)', 'default' => 'Your form was successfully submitted'],
|
||||
'global_messages_errors' => ['label' => '全局错误消息' , 'comment' => '(此项设置可以被组件覆盖)', 'default' => 'There were errors with your submission'],
|
||||
'plugin_help' => '你可以在 Github 仓库中查看插件文档:',
|
||||
'global_hide_button' => '隐藏导航项',
|
||||
'global_hide_button_desc' => '如果要在你的定制插件中使用事件',
|
||||
'section_recaptcha' => 'reCAPTCHA 设置',
|
||||
'recaptcha_site_key' => 'Site key',
|
||||
'recaptcha_secret_key' => 'Secret key',
|
||||
'gdpr_help_title' => '提示信息',
|
||||
'gdpr_help_comment' => '根据欧洲新的 GDPR 法案,你不能无限期地保存用户数据,根据你的需求,在一段时间后,需要需要清除它们。',
|
||||
'gdpr_enable' => '启用 GDPR',
|
||||
'gdpr_days' => '保留数据最多不超过 X 天',
|
||||
],
|
||||
|
||||
'permissions' => [
|
||||
'tab' => '联系表单',
|
||||
'access_records' => '访问已保存的表单数据',
|
||||
'access_exports' => '访问导出功能,导出已保存的数据',
|
||||
'access_settings' => '访问插件配置',
|
||||
'gdpr_cleanup' => '执行 GDPR 数据清理',
|
||||
],
|
||||
|
||||
'mails' => [
|
||||
'form_notification' => ['description' => '表单提交时通知'],
|
||||
'form_autoresponse' => ['description' => '表单提交时自动答复'],
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'recaptcha_error' => 'Cannot validate reCAPTCHA field'
|
||||
],
|
||||
|
||||
'classes' => [
|
||||
'GDPR' => [
|
||||
'alert_gdpr_disabled' => 'GDPR 选项已禁用',
|
||||
'alert_invalid_gdpr' => '无效的 GDPR 天数设置',
|
||||
]
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Models;
|
||||
|
||||
use Model;
|
||||
|
||||
class Record extends Model {
|
||||
|
||||
use \October\Rain\Database\Traits\SoftDelete;
|
||||
|
||||
public $table = 'martin_forms_records';
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
public $attachMany = [
|
||||
'files' => ['System\Models\File', 'public' => false]
|
||||
];
|
||||
|
||||
public function getFormDataArrAttribute() {
|
||||
return (array) json_decode($this->form_data);
|
||||
}
|
||||
|
||||
public function filterGroups() {
|
||||
return Record::orderBy('group')->groupBy('group')->lists('group', 'group');
|
||||
}
|
||||
|
||||
public function getGroupsOptions() {
|
||||
return $this->filterGroups();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Models;
|
||||
|
||||
use Lang, Model;
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Classes\Page;
|
||||
|
||||
class Settings extends Model {
|
||||
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
public $implement = ['System.Behaviors.SettingsModel'];
|
||||
public $settingsCode = 'martin_forms_settings';
|
||||
public $settingsFields = 'fields.yaml';
|
||||
|
||||
// public function __construct() {
|
||||
// $this->attributes = [
|
||||
// 'global_messages_success' => Lang::get('martin.forms::lang.settings.global_messages_success.default'),
|
||||
// 'global_messages_errors' => Lang::get('martin.forms::lang.settings.global_messages_errors.default'),
|
||||
// ];
|
||||
// parent::__construct();
|
||||
// }
|
||||
|
||||
public $rules = [
|
||||
'gdpr_days' => 'required|numeric',
|
||||
];
|
||||
|
||||
public $attributeNames = [
|
||||
'gdpr_days' => 'GDPR',
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="callout fade in callout-warning no-subheader">
|
||||
<div class="header">
|
||||
<i class="icon-warning"></i>
|
||||
<h3>Warning</h3>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>This is an experimental feature to export your stored data in CSV format.</p>
|
||||
<p>Note: file uploads are not exported</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
fields:
|
||||
|
||||
header:
|
||||
type: partial
|
||||
path: $/martin/forms/models/export/_header.htm
|
||||
span: left
|
||||
|
||||
################################################################################################################################################
|
||||
# FILTER SECTION
|
||||
################################################################################################################################################
|
||||
filter_section:
|
||||
label : martin.forms::lang.controllers.exports.filter_section
|
||||
type : section
|
||||
cssClass: m-t-md
|
||||
|
||||
filter_type:
|
||||
label : martin.forms::lang.controllers.exports.filter_type
|
||||
type : switch
|
||||
default: true
|
||||
|
||||
filter_groups:
|
||||
label : martin.forms::lang.controllers.exports.filter_groups
|
||||
span : left
|
||||
type : checkboxlist
|
||||
options: getGroupsOptions
|
||||
trigger:
|
||||
action : hide
|
||||
field : filter_type
|
||||
condition: checked
|
||||
|
||||
filter_date_after:
|
||||
label : martin.forms::lang.controllers.exports.filter_date_after
|
||||
span : left
|
||||
type : datepicker
|
||||
mode : date
|
||||
trigger:
|
||||
action : hide
|
||||
field : filter_type
|
||||
condition: checked
|
||||
|
||||
filter_date_before:
|
||||
label : martin.forms::lang.controllers.exports.filter_date_before
|
||||
span : left
|
||||
type : datepicker
|
||||
mode : date
|
||||
trigger:
|
||||
action : hide
|
||||
field : filter_type
|
||||
condition: checked
|
||||
|
||||
################################################################################################################################################
|
||||
# OPTIONS SECTION
|
||||
################################################################################################################################################
|
||||
options_section:
|
||||
label : martin.forms::lang.controllers.exports.options_section
|
||||
type : section
|
||||
cssClass: m-t-md
|
||||
|
||||
options_delimiter:
|
||||
label : martin.forms::lang.controllers.exports.options_delimiter
|
||||
comment: martin.forms::lang.controllers.exports.options_delimiter_com
|
||||
span : right
|
||||
type : switch
|
||||
default: false
|
||||
|
||||
options_metadata:
|
||||
label : martin.forms::lang.controllers.exports.options_metadata
|
||||
comment: martin.forms::lang.controllers.exports.options_metadata_com
|
||||
span : left
|
||||
type : switch
|
||||
default: true
|
||||
|
||||
options_utf:
|
||||
label : martin.forms::lang.controllers.exports.options_utf
|
||||
comment: martin.forms::lang.controllers.exports.options_utf_com
|
||||
span : right
|
||||
type : switch
|
||||
default: true
|
||||
|
||||
options_deleted:
|
||||
label : martin.forms::lang.controllers.exports.options_deleted
|
||||
span : left
|
||||
type : switch
|
||||
default: false
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
columns:
|
||||
|
||||
id:
|
||||
label : martin.forms::lang.controllers.records.columns.id
|
||||
searchable: true
|
||||
|
||||
group:
|
||||
label : martin.forms::lang.controllers.records.columns.group
|
||||
searchable: true
|
||||
|
||||
ip:
|
||||
label : martin.forms::lang.controllers.records.columns.ip
|
||||
searchable: true
|
||||
|
||||
form_data:
|
||||
label : martin.forms::lang.controllers.records.columns.form_data
|
||||
type : partial
|
||||
path : $/martin/forms/models/record/fields/_stored_fields.htm
|
||||
searchable: true
|
||||
|
||||
files:
|
||||
label : martin.forms::lang.controllers.records.columns.files
|
||||
type : partial
|
||||
path : $/martin/forms/models/record/fields/_files_fields.htm
|
||||
searchable: false
|
||||
sortable : false
|
||||
#invisible : true
|
||||
|
||||
created_at:
|
||||
label: martin.forms::lang.controllers.records.columns.created_at
|
||||
type: datetime
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?php echo count($record->files) ?>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?php echo implode(', ', array_keys($record->form_data_arr)) ?>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="callout callout-info no-subheader">
|
||||
<div class="header">
|
||||
<i class="icon-info"></i>
|
||||
<h3><?php echo e(trans('martin.forms::lang.settings.gdpr_help_title')) ?></h3>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p><?php echo e(trans('martin.forms::lang.settings.gdpr_help_comment')) ?></p>
|
||||
<p>More info at <a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation" target="_blank">Wikipedia</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<div class="callout callout-info no-subheader">
|
||||
<div class="header">
|
||||
<i class="icon-info"></i>
|
||||
<h3><?php echo e(trans('martin.forms::lang.plugin.name')) ?></h3>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p><?php echo e(trans('martin.forms::lang.settings.plugin_help')) ?> <a href="https://github.com/skydiver/october-plugin-forms" target="_blank">https://github.com/skydiver/october-plugin-forms</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<div class="callout callout-info no-subheader">
|
||||
<div class="header">
|
||||
<i class="icon-info"></i>
|
||||
<h3>How to configure</h3>
|
||||
</div>
|
||||
<div class="content">
|
||||
<ol>
|
||||
<li>Go to <a target="_blank" href="https://www.google.com/recaptcha/admin">https://www.google.com/recaptcha/admin</a>
|
||||
<li>Register a new site, set <strong>Label</strong> and <strong>Domains</strong></li>
|
||||
<li>Copy/paste <strong>"Site key"</strong> & <strong>"Secret key"</strong></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
tabs:
|
||||
fields:
|
||||
|
||||
plugin_help:
|
||||
type : partial
|
||||
path : $/martin/forms/models/settings/_plugin_help.htm
|
||||
tab : martin.forms::lang.settings.tabs.general
|
||||
|
||||
global_hide_button:
|
||||
label : martin.forms::lang.settings.global_hide_button
|
||||
comment: martin.forms::lang.settings.global_hide_button_desc
|
||||
type : switch
|
||||
default: false
|
||||
tab : martin.forms::lang.settings.tabs.general
|
||||
|
||||
recaptcha_help:
|
||||
type : partial
|
||||
path : $/martin/forms/models/settings/_recaptcha_help.htm
|
||||
tab : martin.forms::lang.settings.tabs.recaptcha
|
||||
|
||||
recaptcha_site_key:
|
||||
label: martin.forms::lang.settings.recaptcha_site_key
|
||||
span : left
|
||||
tab : martin.forms::lang.settings.tabs.recaptcha
|
||||
|
||||
recaptcha_secret_key:
|
||||
label: martin.forms::lang.settings.recaptcha_secret_key
|
||||
span : right
|
||||
tab : martin.forms::lang.settings.tabs.recaptcha
|
||||
|
||||
gdpr_help:
|
||||
type : partial
|
||||
path : $/martin/forms/models/settings/_gdpr_help.htm
|
||||
tab : martin.forms::lang.settings.tabs.gdpr
|
||||
|
||||
gdpr_enable:
|
||||
label : martin.forms::lang.settings.gdpr_enable
|
||||
type : switch
|
||||
default: false
|
||||
tab : martin.forms::lang.settings.tabs.gdpr
|
||||
|
||||
gdpr_days:
|
||||
label : martin.forms::lang.settings.gdpr_days
|
||||
span : left
|
||||
default: 120
|
||||
tab : martin.forms::lang.settings.tabs.gdpr
|
||||
trigger:
|
||||
action: show
|
||||
field: gdpr_enable
|
||||
condition: checked
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="../../../tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Plugin Unit Test Suite">
|
||||
<directory>./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Tests\Classes;
|
||||
|
||||
use Backend;
|
||||
use BackendAuth;
|
||||
use PluginTestCase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use System\Classes\PluginManager;
|
||||
use Backend\Models\User;
|
||||
use Martin\Forms\Classes\BackendHelpers;
|
||||
|
||||
class BackendHelpersTest extends PluginTestCase {
|
||||
|
||||
use RefreshDatabase;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
PluginManager::instance()->bootAll(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Get backend URL
|
||||
*/
|
||||
public function testGetBackendUrl() {
|
||||
$this->_loginUser();
|
||||
$expect = Backend::url("martin/forms/records");
|
||||
$bh = new BackendHelpers();
|
||||
$this->assertEquals($expect, $bh->getBackendURL([
|
||||
'martin.forms.access_records' => 'martin/forms/records',
|
||||
'martin.forms.access_exports' => 'martin/forms/exports'
|
||||
], 'martin.forms.access_records'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Convert PHP array to HTML list
|
||||
*/
|
||||
public function testArray2Ul() {
|
||||
$list = [
|
||||
'item1' => 'Item 1',
|
||||
'item2' => ['item21' => 'Item 2.1', 'item22' => 'Item 2.2', 'item23' => 'Item 2.3'],
|
||||
'item3' => 'Item 3',
|
||||
];
|
||||
$expected = '<li>Item 1</li><li>item2<ul><li>Item 2.1</li><li>Item 2.2</li><li>Item 2.3</li></ul></li><li>Item 3</li>';
|
||||
$bh = new BackendHelpers();
|
||||
$this->assertEquals($expected, $bh->array2ul($list));
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Anonymize IPv4 address
|
||||
*/
|
||||
public function testAnonymizeIPv4() {
|
||||
$bh = new BackendHelpers();
|
||||
$this->assertEquals('8.8.8.0', $bh->anonymizeIPv4('8.8.8.8'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Replace string containing curly braces
|
||||
*/
|
||||
public function testReplaceTokenValid() {
|
||||
$bh = new BackendHelpers();
|
||||
$this->assertEquals('includes 50 string', $bh->replaceToken('record.id', '50', 'includes {{ record.id }} string'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Replace string not containing curly braces
|
||||
*/
|
||||
public function testReplaceTokenNoBraces() {
|
||||
$bh = new BackendHelpers();
|
||||
$this->assertEquals('includes record.id string', $bh->replaceToken('record.id', '50', 'includes record.id string'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login backend user
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _loginUser() {
|
||||
$user = User::create([
|
||||
'email' => 'testuser@testcompany.com',
|
||||
'login' => 'testuser',
|
||||
'password' => 'superpassword',
|
||||
'password_confirmation' => 'superpassword',
|
||||
]);
|
||||
BackendAuth::login($user);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Tests\Classes;
|
||||
|
||||
use PluginTestCase;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use System\Classes\PluginManager;
|
||||
use Martin\Forms\Classes\GDPR;
|
||||
use Martin\Forms\Models\Record;
|
||||
use Martin\Forms\Models\Settings;
|
||||
|
||||
class GDPRTest extends PluginTestCase {
|
||||
|
||||
use RefreshDatabase;
|
||||
|
||||
private $_record;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
PluginManager::instance()->bootAll(true);
|
||||
Record::unguard();
|
||||
$this->_record = Record::create([
|
||||
'group' => 'test group',
|
||||
'created_at' => Carbon::now()->subDays(20),
|
||||
'updated_at' => Carbon::now()->subDays(20),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox GDPR cleanup disabled
|
||||
*/
|
||||
public function testCleanRecordsDisabled() {
|
||||
Settings::set([
|
||||
'gdpr_enable' => false,
|
||||
'gdpr_days' => 10
|
||||
]);
|
||||
$gdpr = new GDPR();
|
||||
$gdpr->cleanRecords();
|
||||
$totalRecords = Record::all()->count();
|
||||
$this->assertEquals(1, $totalRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox GDPR cleanup with older records
|
||||
*/
|
||||
public function testCleanRecordsWithOlder() {
|
||||
Settings::set([
|
||||
'gdpr_enable' => true,
|
||||
'gdpr_days' => 10
|
||||
]);
|
||||
$gdpr = new GDPR();
|
||||
$gdpr->cleanRecords();
|
||||
$totalRecords = Record::all()->count();
|
||||
$this->assertEquals(0, $totalRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox GDPR cleanup without older records
|
||||
*/
|
||||
public function testCleanRecordsWithoutOlder() {
|
||||
Settings::set([
|
||||
'gdpr_enable' => true,
|
||||
'gdpr_days' => 30
|
||||
]);
|
||||
$gdpr = new GDPR();
|
||||
$gdpr->cleanRecords();
|
||||
$totalRecords = Record::all()->count();
|
||||
$this->assertEquals(1, $totalRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox GDPR cleanup with invalid days parameter
|
||||
* @expectedException October\Rain\Database\ModelException
|
||||
*/
|
||||
public function testCleanRecordsInvalidDays() {
|
||||
Settings::set([
|
||||
'gdpr_enable' => true,
|
||||
'gdpr_days' => 'INVALID'
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Tests\Classes;
|
||||
|
||||
use PluginTestCase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use System\Classes\PluginManager;
|
||||
use Martin\Forms\Classes\UnreadRecords;
|
||||
use Martin\Forms\Models\Record;
|
||||
|
||||
class UnreadRecordsTest extends PluginTestCase {
|
||||
|
||||
use RefreshDatabase;
|
||||
|
||||
private $_record;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
PluginManager::instance()->bootAll(true);
|
||||
Record::unguard();
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Get total unread records with unread records
|
||||
*/
|
||||
public function testGetTotal() {
|
||||
$record = Record::create([
|
||||
'group' => 'test group',
|
||||
]);
|
||||
$unread = new UnreadRecords();
|
||||
$this->assertEquals(1, $record->id);
|
||||
$this->assertEquals('test group', $record->group);
|
||||
$this->assertEquals(1, $unread->getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Get total unread records without unread records
|
||||
*/
|
||||
public function testGetTotalNoUnread() {
|
||||
$record = Record::create([
|
||||
'group' => 'test group',
|
||||
'unread' => 0
|
||||
]);
|
||||
$unread = new UnreadRecords();
|
||||
$this->assertEquals(1, $record->id);
|
||||
$this->assertEquals('test group', $record->group);
|
||||
$this->assertEquals(0, $unread->getTotal());
|
||||
$this->assertNull($unread->getTotal());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
<?php namespace Martin\Forms\Traits;
|
||||
|
||||
use Input;
|
||||
use Request;
|
||||
use Response;
|
||||
use Validator;
|
||||
use ValidationException;
|
||||
use ApplicationException;
|
||||
use System\Models\File;
|
||||
use October\Rain\Support\Collection;
|
||||
use Exception;
|
||||
use October\Rain\Filesystem\Definitions;
|
||||
|
||||
trait ComponentUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $attribute;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $sessionKey;
|
||||
|
||||
public function bindModel($attribute, $model)
|
||||
{
|
||||
if (is_callable($model))
|
||||
$model = $model();
|
||||
|
||||
$this->model = $model;
|
||||
$this->attribute = $attribute;
|
||||
|
||||
if ($this->model) {
|
||||
$relationType = $this->model->getRelationType($attribute);
|
||||
$this->isMulti = ($relationType == 'attachMany' || $relationType == 'morphMany');
|
||||
$this->isBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function setPopulated($model)
|
||||
{
|
||||
$list = $this->isMulti ? $model : new Collection([$model]);
|
||||
|
||||
$list->each(function($file) {
|
||||
$this->decorateFileAttributes($file);
|
||||
});
|
||||
|
||||
$this->fileList = $list;
|
||||
$this->singleFile = $list->first();
|
||||
}
|
||||
|
||||
public function isPopulated()
|
||||
{
|
||||
if (!$this->fileList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->fileList->count() > 0;
|
||||
}
|
||||
|
||||
public function getFileList()
|
||||
{
|
||||
/*
|
||||
* Use deferred bindings
|
||||
*/
|
||||
if ($sessionKey = $this->getSessionKey()) {
|
||||
$list = $deferredQuery = $this->model
|
||||
->{$this->attribute}()
|
||||
->withDeferred($sessionKey)
|
||||
->orderBy('id', 'desc')
|
||||
->get();
|
||||
}
|
||||
else {
|
||||
$list = $this->model
|
||||
->{$this->attribute}()
|
||||
->orderBy('id', 'desc')
|
||||
->get();
|
||||
}
|
||||
|
||||
if (!$list) {
|
||||
$list = new Collection;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decorate each file with thumb
|
||||
*/
|
||||
$list->each(function($file) {
|
||||
$this->decorateFileAttributes($file);
|
||||
});
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function checkUploadAction()
|
||||
{
|
||||
if (!($uniqueId = Request::header('X-OCTOBER-FILEUPLOAD')) || $uniqueId != $this->alias) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!Input::hasFile('file_data')) {
|
||||
throw new ApplicationException('File missing from request');
|
||||
}
|
||||
|
||||
$uploadedFile = Input::file('file_data');
|
||||
|
||||
|
||||
$validationRules = ['max:'.File::getMaxFilesize()];
|
||||
if ($fileTypes = $this->processFileTypes()) {
|
||||
$validationRules[] = 'extensions:'.$fileTypes;
|
||||
}
|
||||
|
||||
$validation = Validator::make(
|
||||
['file_data' => $uploadedFile],
|
||||
['file_data' => $validationRules]
|
||||
);
|
||||
|
||||
if ($validation->fails()) {
|
||||
throw new ValidationException($validation);
|
||||
}
|
||||
|
||||
if (!$uploadedFile->isValid()) {
|
||||
throw new ApplicationException(sprintf('File %s is not valid.', $uploadedFile->getClientOriginalName()));
|
||||
}
|
||||
|
||||
$file = new File;
|
||||
$file->data = $uploadedFile;
|
||||
$file->is_public = false;
|
||||
$file->save();
|
||||
|
||||
$this->model->{$this->attribute}()->add($file, $this->getSessionKey());
|
||||
|
||||
$file = $this->decorateFileAttributes($file);
|
||||
|
||||
// $result = [
|
||||
// 'id' => $file->id,
|
||||
// 'thumb' => $file->thumbUrl,
|
||||
// 'path' => $file->pathUrl
|
||||
// ];
|
||||
|
||||
$result = 'ok';
|
||||
|
||||
return Response::json($result, 200);
|
||||
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
return Response::json($ex->getMessage(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSessionKey()
|
||||
{
|
||||
return !!$this->property('deferredBinding')
|
||||
? post('_session_key', $this->sessionKey)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified accepted file types, or the default
|
||||
* based on the mode. Image mode will return:
|
||||
* - jpg,jpeg,bmp,png,gif,svg
|
||||
* @return string
|
||||
*/
|
||||
protected function processFileTypes($includeDot = false)
|
||||
{
|
||||
$types = $this->property('fileTypes', '*');
|
||||
|
||||
if (!$types || $types == '*') {
|
||||
$types = implode(',', Definitions::get('defaultExtensions'));
|
||||
}
|
||||
|
||||
if (!is_array($types)) {
|
||||
$types = explode(',', $types);
|
||||
}
|
||||
|
||||
$types = array_map(function($value) use ($includeDot) {
|
||||
$value = trim($value);
|
||||
|
||||
if (substr($value, 0, 1) == '.') {
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
|
||||
if ($includeDot) {
|
||||
$value = '.'.$value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $types);
|
||||
|
||||
return implode(',', $types);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<?php namespace Martin\Forms\Traits;
|
||||
|
||||
use Input;
|
||||
use Cms\Classes\ComponentBase;
|
||||
use System\Models\File;
|
||||
use System\Classes\CombineAssets;
|
||||
use ApplicationException;
|
||||
|
||||
trait FileUploader
|
||||
{
|
||||
|
||||
use \Martin\Forms\Traits\ComponentUtils;
|
||||
|
||||
public $maxSize;
|
||||
public $placeholderText;
|
||||
public $removeText;
|
||||
|
||||
/**
|
||||
* Supported file types.
|
||||
* @var array
|
||||
*/
|
||||
public $fileTypes;
|
||||
|
||||
/**
|
||||
* @var bool Has the model been bound.
|
||||
*/
|
||||
protected $isBound = false;
|
||||
|
||||
/**
|
||||
* @var bool Is the related attribute a "many" type.
|
||||
*/
|
||||
public $isMulti = false;
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*/
|
||||
public $fileList;
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
public $singleFile;
|
||||
|
||||
public function componentDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'File Uploader',
|
||||
'description' => 'Upload a file'
|
||||
];
|
||||
}
|
||||
|
||||
public function defineProperties()
|
||||
{
|
||||
return [
|
||||
'placeholderText' => [
|
||||
'title' => 'Placeholder text',
|
||||
'description' => 'Wording to display when no file is uploaded',
|
||||
'default' => 'Click or drag files to upload',
|
||||
'type' => 'string',
|
||||
],
|
||||
'removeText' => [
|
||||
'title' => 'Remove Popup text',
|
||||
'description' => 'Wording to display in the popup when you remove file',
|
||||
'default' => 'Are you sure ?',
|
||||
'type' => 'string',
|
||||
],
|
||||
'maxSize' => [
|
||||
'title' => 'Max file size (MB)',
|
||||
'description' => 'The maximum file size that can be uploaded in megabytes.',
|
||||
'default' => '5',
|
||||
'type' => 'string',
|
||||
],
|
||||
'fileTypes' => [
|
||||
'title' => 'Supported file types',
|
||||
'description' => 'File extensions separated by commas (,) or star (*) to allow all types.',
|
||||
'default' => '*',
|
||||
'type' => 'string',
|
||||
],
|
||||
'deferredBinding' => [
|
||||
'title' => 'Use deferred binding',
|
||||
'description' => 'If checked the associated model must be saved for the upload to be bound.',
|
||||
'type' => 'checkbox',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->fileTypes = $this->processFileTypes(true);
|
||||
$this->maxSize = $this->property('maxSize');
|
||||
$this->placeholderText = $this->property('placeholderText');
|
||||
$this->removeText = $this->property('removeText');
|
||||
}
|
||||
|
||||
public function onRun()
|
||||
{
|
||||
$this->addCss('assets/css/uploader.css');
|
||||
$this->addJs('assets/vendor/dropzone/dropzone.js');
|
||||
$this->addJs('assets/js/uploader.js');
|
||||
|
||||
if ($result = $this->checkUploadAction()) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->fileList = $fileList = $this->getFileList();
|
||||
$this->singleFile = $fileList->first();
|
||||
}
|
||||
|
||||
public function onRender()
|
||||
{
|
||||
if (!$this->isBound) {
|
||||
throw new ApplicationException('There is no model bound to the uploader!');
|
||||
}
|
||||
|
||||
if ($populated = $this->property('populated')) {
|
||||
$this->setPopulated($populated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the bespoke attributes used internally by this widget.
|
||||
* - thumbUrl
|
||||
* - pathUrl
|
||||
* @return System\Models\File
|
||||
*/
|
||||
protected function decorateFileAttributes($file)
|
||||
{
|
||||
$file->pathUrl = $file->thumbUrl = $file->getPath();
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
public function onRemoveAttachment()
|
||||
{
|
||||
if (($file_id = post('file_id')) && ($file = File::find($file_id))) {
|
||||
$this->model->{$this->attribute}()->remove($file, $this->getSessionKey());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class AddGroupField extends Migration {
|
||||
|
||||
public function up() {
|
||||
Schema::table('martin_forms_records', function ($table) {
|
||||
$table->string('group')->default('(Empty)')->after('id');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public function down() {
|
||||
if(Schema::hasColumn('martin_forms_records', 'group')) {
|
||||
Schema::table('martin_forms_records', function ($table) {
|
||||
$table->dropColumn('group');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
use Martin\Forms\Models\Record;
|
||||
|
||||
class AddUnreadField extends Migration {
|
||||
|
||||
public function up() {
|
||||
|
||||
// CREATE FIELD
|
||||
Schema::table('martin_forms_records', function ($table) {
|
||||
$table->boolean('unread')->default(1)->after('ip');
|
||||
});
|
||||
|
||||
// UPDATE EXISTING RECORDS TO READED
|
||||
Record::where('unread', 1)->update(['unread' => 0]);
|
||||
|
||||
}
|
||||
|
||||
public function down() {
|
||||
if(Schema::hasColumn('martin_forms_records', 'unread')) {
|
||||
Schema::table('martin_forms_records', function ($table) {
|
||||
$table->dropColumn('unread');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Martin\Forms\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Schema\Blueprint;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class CreateRecordsTable extends Migration {
|
||||
|
||||
public function up() {
|
||||
Schema::create('martin_forms_records', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->text('form_data')->nullable();
|
||||
$table->string('ip')->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down() {
|
||||
Schema::dropIfExists('martin_forms_records');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
1.0.0:
|
||||
- First version of Magic Forms
|
||||
- create_records_table.php
|
||||
1.0.1:
|
||||
- Added CSRF protection
|
||||
1.1.0:
|
||||
- Added reCAPTCHA
|
||||
1.1.1:
|
||||
- Fix when using reCAPTCHA + allowed fields
|
||||
1.1.2:
|
||||
- Filter forms records
|
||||
- Search inside stored data
|
||||
- Organize your forms on custom groups
|
||||
- add_group_field.php
|
||||
1.2.0:
|
||||
- Export stored data in CSV format
|
||||
1.2.1:
|
||||
- Auto-response email on form submit
|
||||
- Added Turkish language
|
||||
1.2.2:
|
||||
- Override notifications and auto-response email subjects
|
||||
1.2.3:
|
||||
- New option to reset form after successfully submit
|
||||
- Fixed Empty AJAX Form template
|
||||
- Support for Translate plugin
|
||||
- Added plugin documentation
|
||||
1.2.4:
|
||||
- Added detailed reCAPTCHA help
|
||||
1.3.0:
|
||||
- AJAX file uploads
|
||||
1.3.1:
|
||||
- Added lang pt-br
|
||||
1.3.2:
|
||||
- Fixed multiples reCAPTCHAs on same page
|
||||
1.3.3:
|
||||
- Fixed record detail page when form data contains an array
|
||||
- Updated documentations
|
||||
1.3.4:
|
||||
- New "Anonymize IP" option
|
||||
1.3.5:
|
||||
- New option "Redirect on successful submit"
|
||||
1.3.6:
|
||||
- French translation
|
||||
- Support Translate plugin on reCAPTCHA
|
||||
- reCAPTCHA validation enhancements
|
||||
1.3.7:
|
||||
- Displaying errors with fields (inline errors)
|
||||
- Show uploads as list
|
||||
1.3.8:
|
||||
- Fixed handling arrays (radio inputs) in notification email
|
||||
1.3.9:
|
||||
- Use custom mail templates
|
||||
- Execute custom JavaScript on form success or error
|
||||
1.4.0:
|
||||
- Added Events (please, refer to docs) [thanks to therealkevinard]
|
||||
1.4.1:
|
||||
- New option "Reply To"
|
||||
1.4.2:
|
||||
- Escape HTML characters on the view records page [thanks to Andre]
|
||||
- New option to sanitize form data (check security docs for more info)
|
||||
- Added option to send blind carbon copy in notifications email
|
||||
1.4.3:
|
||||
- Fixes related to October Build 420
|
||||
- Added "Unread Records" counter
|
||||
- Fixed errors when only BCC addresses are supplied
|
||||
- New setting "hide navigation item"
|
||||
- add_unread_field.php
|
||||
1.4.4:
|
||||
- Use custom partials for Success and Error messages
|
||||
1.4.4.1:
|
||||
- Fix with notifications emails
|
||||
1.4.5:
|
||||
- Mail class code refactoring
|
||||
- Access submited data on auto-response email template
|
||||
1.4.5.1:
|
||||
- Store form data without escaping unicode [thanks to panakour]
|
||||
1.4.6:
|
||||
- New option to skip saving forms data on database.
|
||||
- Possibility to change the text on the remove file popup [thanks to ShiroeSama]
|
||||
1.4.6.1:
|
||||
- Changed database field from json to text to support MySQL 5.5
|
||||
1.4.7:
|
||||
- you can use your form variables on notification mail subject [thanks to Alex360hd]
|
||||
- fix custom subject on email template [Thanks to matteotrubini]
|
||||
- fix email bug when not storing on db [Thanks JurekRaben]
|
||||
- skip url redirect validation [Thanks to EleRam]
|
||||
1.4.8:
|
||||
- added GDPR cleanup feature [thanks to Alex360hd]
|
||||
1.4.9:
|
||||
- fix on replaceToken function when replacement is null [thanks to leonaze]
|
||||
1.4.9.1:
|
||||
- fix a nullable type error on PHP 7.0
|
||||
1.4.9.2:
|
||||
- bugfix when a form field array has more than 2 levels of depth
|
||||
1.4.10:
|
||||
- improvements related to event functionality
|
||||
1.4.11:
|
||||
- added Laravel custom attributes to form validation [thanks to geekfil]
|
||||
- updated french translation [thanks to FelixINX]
|
||||
1.4.12:
|
||||
- use form variables on auto-response mail subject [thanks to jiargei]
|
||||
1.4.13:
|
||||
- pass an array with form errors to JavaScript [thanks to multiwebinc]
|
||||
1.4.14:
|
||||
- fixed error with empty auto-response subject
|
||||
1.4.15:
|
||||
- enhancements related to saving record and events [thanks to boxxroom]
|
||||
1.4.16:
|
||||
- added chinese translation [thanks to everyx]
|
||||
1.4.17:
|
||||
- allowing sanitize to work recursively [thanks to multiwebinc]
|
||||
1.4.18:
|
||||
- export records enhancements [thanks to Fosphatic]
|
||||
- recaptcha locale fix [thanks to MaTToX3]
|
||||
1.4.19:
|
||||
- added russian translation [thanks to FlusherDock1]
|
||||
- sort records by date fix [thanks to mjauvin]
|
||||
1.4.20:
|
||||
- added invisible reCAPTCHA [thanks to mjauvin]
|
||||
- new option to set custom date format on emails subject
|
||||
1.5.0:
|
||||
- fixes related to October Build 469 [thanks to mjauvin]
|
||||
- fix when CSRF check is disabled [thanks to rechik]
|
||||
- php linting and cleanup
|
||||
1.5.1:
|
||||
- email templates improvemenrs [thanks to mjauvin]
|
||||
- added german translation [thanks to Fosphatic]
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
subject = "Thank you for contacting us!"
|
||||
==
|
||||
Thank you for contacting us! We have received your request and are working on responding to you as soon as possible.
|
||||
If you have any additional information to add to this case, please reply to this email.
|
||||
Thanks in advance for your patience and support.
|
||||
==
|
||||
<p>Thank you for contacting us! We have received your request and are working on responding to you as soon as possible.</p>
|
||||
<p>If you have any additional information to add to this case, please reply to this email.</p>
|
||||
<p>Thanks in advance for your patience and support.</p>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
subject = "Magic Forms :: new form submited"
|
||||
==
|
||||
Record #{{ id}}
|
||||
{% for label,value in data %}
|
||||
{{ label|capitalize }}: {% if value is iterable %}{% for val in value %}{{ val|nl2br }}{% if not loop.last %}, {% endif %}{% endfor %}{% else %}{{ value|nl2br }}{% endif %}
|
||||
{% endfor %}
|
||||
==
|
||||
<h3>Record #{{ id}}</h3>
|
||||
|
||||
<table style="width:60%; border-collapse:collapse; ">
|
||||
{% for label,value in data %}
|
||||
<tr>
|
||||
<td style="width:20%; background-color:#ECF0F1; text-align:right; padding:10px; border:solid 1px #D4D8DA; font-weight:bold;">{{ label|capitalize }}:</td>
|
||||
<td style="width:80%; background-color:#F9F9F9; padding:10px; border:solid 1px #D4D8DA;">
|
||||
{% if value is iterable %}
|
||||
{% for val in value %}
|
||||
{{ val|nl2br }}
|
||||
{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ value|nl2br }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<div style="width:60%; text-align:right;">
|
||||
<p style="font-size:0.8em; color:#888; font-weight:bold;">IP: {{ ip }}</p>
|
||||
<p style="font-size:0.8em; color:#888; font-weight:bold;">Time: {{ date }}</p>
|
||||
</div>
|
||||
Loading…
Reference in New Issue