Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion public/vendor/exment/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -3847,6 +3847,7 @@ body .navbar-nav > .notifications-menu > .dropdown-menu, .navbar-nav > .messages

.has-error .btn-valuemodal, .has-error .text-valuemodal, .has-error .checkbox, .has-error .checkbox-inline, .has-error .control-label, .has-error .help-block, .has-error .radio, .has-error .radio-inline {
color: #dd4b39 !important;
word-break: break-all;
}

.has-error .select2-container--default .select2-selection--single, .has-error .select2-selection .select2-selection--single {
Expand All @@ -3857,6 +3858,10 @@ body .navbar-nav > .notifications-menu > .dropdown-menu, .navbar-nav > .messages
color: #dd4b39;
}

.has-error .help-block, .has-error .help-block .fa {
color: #737373 !important;
}

.column-__actions__ {
min-width: 50px;
}
Expand Down Expand Up @@ -4245,4 +4250,8 @@ label.radio-inline:has(input[readonly][type=radio]) {

label.checkboxone-label:has(input[readonly][type=checkbox]) {
pointer-events: none;
}
}

.btn.btn-file>input[type='file'] {
font-size: 0px !important;
}
32 changes: 32 additions & 0 deletions public/vendor/exment/js/file-required.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* File Required Handler
* Handle adding required attribute when file is removed from Bootstrap Fileinput
*/
(function($) {
'use strict';

$(document).ready(function() {
// Handle click on kv-file-remove button
$(document).on('click', '.kv-file-remove', function(event) {
var $removeBtn = $(this);
// Find the closest file input (for both file and image types)
var $fileInputContainer = $removeBtn.closest('.file-input');
if ($fileInputContainer.length > 0) {
var $fileInput = $fileInputContainer.find('input[type="file"][data-column_type="file"], input[type="file"][data-column_type="image"]');
if ($fileInput.length > 0) {
// Wait a bit for the file to be actually removed, then check if all files are removed
setTimeout(function() {
// Check if there are any remaining file previews
var hasFiles = $fileInputContainer.find('.file-preview-frame:not(.file-preview-initial)').length > 0;
if (!hasFiles) {
// No files left, add required attribute
$fileInput.attr('required', '1');
$fileInput.prop('required', true);
}
}, 100);
}
}
});
});

})(jQuery);
255 changes: 255 additions & 0 deletions public/vendor/exment/js/hasmanytable-validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/**
* Has-Many Table Validation
*/
(function($) {
'use strict';

function keyFromName(name) {
if (!name) return null;
var m = name.match(/\[value\]\[([^\]]+)\]|\[([^\]]+)\]$/);
return m ? (m[1] || m[2]) : null;
}

function isHidden($el) {
if (!$el || !$el.length) return false;
if ($el.is(':hidden')) return true;
return $el.parents().addBack().filter(function() {
return $(this).css('visibility') === 'hidden';
}).length > 0;
}

function outerCell($field) {
var $row = $field.closest('tr.has-many-table-row');
if ($row.length) {
var $td = $row.children('td').filter(function() {
return this.contains($field[0]);
}).first();
if ($td.length) return $td;
}
return $field.closest('td');
}

function colPos($td) {
var pos = 0;
$td.prevAll('td').each(function() {
var span = parseInt($(this).attr('colspan'), 10);
pos += isNaN(span) ? 1 : span;
});
return pos;
}

function headerAt($table, pos) {
var $hit = $();
var cur = 0;
$table.find('thead tr').first().children('th').each(function() {
var span = parseInt($(this).attr('colspan'), 10);
var w = isNaN(span) ? 1 : span;
if (pos >= cur && pos < cur + w) {
$hit = $(this);
return false;
}
cur += w;
});
return $hit;
}

function headerText($th) {
return $th.clone().find('i.fa-info-circle, i.fa, .fa').remove().end().text().trim();
}

function fieldLabel($field) {
var nameKey = keyFromName($field.attr('name'));
var $table = $field.closest('table.has-many-table');

var label = '';
if ($table.length) {
var $td = outerCell($field);
if ($td.length) label = headerText(headerAt($table, colPos($td)));
}

if (label && nameKey && (/^(Action|操作)$/i).test(label)) return nameKey;
if (label) return label;

var $lbl = $field.closest('.form-group').find('label');
if ($lbl.length) return $lbl.text().trim();

return $field.attr('placeholder') || nameKey || $field.attr('name') || 'Unknown field';
}

function tableName($table) {
var $h = $table.closest('.has-many-table-div').find('.field-header');
if ($h.length) return $h.text().trim();

var cls = ($table.attr('class') || '').match(/\bhas-many-table-([^\s]+?)-table\b/);
if (cls && cls[1]) {
return cls[1].replace(/_/g, ' ').replace(/\b\w/g, function(c) { return c.toUpperCase(); });
}
return 'Table';
}

function isEmptyRequiredValue($f) {
if (!$f || !$f.length) return true;
if ($f.is(':disabled')) return false;

if ($f.is('input[type="checkbox"], input[type="radio"]')) {
return !$f.is(':checked');
}

if ($f.is('select')) {
var v = $f.val();
if (Array.isArray(v)) return v.length === 0;
return $.trim(v) === '';
}

var val = $f.val();
return val == null || $.trim(val) === '';
}

function findHiddenRequired() {
var bad = [];

$('.has-many-table').each(function() {
var $table = $(this);
var tname = tableName($table);

$table.find('tbody tr.has-many-table-row').not('.template').each(function(i) {
var rowNo = i + 1;

$(this).find('input[required], select[required], textarea[required]').each(function() {
var $f = $(this);
if ($f.is('input[type="hidden"]')) {
var n = $f.attr('name') || '';
if (n.indexOf('[value][') === -1) return;
}

// Only block when required field is hidden AND empty
if (!isEmptyRequiredValue($f)) return;

if (isHidden($f) || isHidden($f.closest('td'))) {
bad.push({ table: tname, row: rowNo, field: fieldLabel($f), element: $f });
}
});
});
});

return bad;
}

function showAlert(fields) {
if (!fields.length) return;

function escHtml(s) {
return String(s).replace(/[&<>"']/g, function(c) {
return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
});
}

function hval(id, fallback) {
var $el = $('#' + id);
return $el.length ? $el.val() : fallback;
}

var TITLE = hval('exment_hm_validation_title', 'バリデーションエラー');
var PLAIN_PREFIX = hval('exment_hm_validation_plain_prefix', '以下の必須項目が非表示になっています:');
var HTML_PREFIX = hval('exment_hm_validation_html_prefix', '以下の必須項目が非表示になっており、表示する必要があります:');
var OK_TEXT = hval('exment_hm_validation_ok', 'OK');
var ROW_TEXT = hval('exment_common_row', '行');

// Group: table -> row -> [field...]
var byTableRow = {};
fields.forEach(function(x) {
byTableRow[x.table] = byTableRow[x.table] || {};
byTableRow[x.table][x.row] = byTableRow[x.table][x.row] || [];
byTableRow[x.table][x.row].push(x.field);
});

var lines = [];
Object.keys(byTableRow).forEach(function(t) {
lines.push(t + ':');

Object.keys(byTableRow[t]).sort(function(a, b) {
return parseInt(a, 10) - parseInt(b, 10);
}).forEach(function(r) {
var parts = byTableRow[t][r].map(function(f) {
return '• ' + ROW_TEXT + ' ' + r + ': ' + f;
});
lines.push(parts.join(' '));
});
});

var plain = PLAIN_PREFIX + '\n\n' + lines.join('\n');

if (typeof toastr !== 'undefined') {
var toastHtml = escHtml(plain).replace(/\n/g, '<br>');
toastr.error(toastHtml, TITLE, {
timeOut: 10000,
extendedTimeOut: 5000,
closeButton: true,
progressBar: true,
positionClass: 'toast-top-right',
escapeHtml: false
});
return;
}

var html = '<div style="text-align: left;"><strong>' + escHtml(HTML_PREFIX) + '</strong><br><br>';
Object.keys(byTableRow).forEach(function(t) {
html += '<strong>' + t + ':</strong><ul style="margin: 5px 0 15px 20px;">';
Object.keys(byTableRow[t]).sort(function(a, b) {
return parseInt(a, 10) - parseInt(b, 10);
}).forEach(function(r) {
var parts = byTableRow[t][r].map(function(f) {
return escHtml(ROW_TEXT) + ' ' + r + ': <em>' + escHtml(f) + '</em>';
});
html += '<li>' + parts.join(' / ') + '</li>';
});
html += '</ul>';
});
html += '</div>';

if (typeof swal !== 'undefined') {
swal({ title: TITLE, text: html, html: true, type: 'error', confirmButtonText: OK_TEXT });
} else if (typeof Swal !== 'undefined') {
Swal.fire({ title: TITLE, html: html, icon: 'error', confirmButtonText: OK_TEXT });
} else {
alert(plain);
}
}

function init() {
var $form = $('form').has('.has-many-table');
if (!$form.length) return;

$form.on('submit', function(e) {
var fields = findHiddenRequired();
if (!fields.length) return;

e.preventDefault();
e.stopImmediatePropagation();

showAlert(fields);

if (fields[0].element) {
$('html, body').animate({
scrollTop: fields[0].element.closest('.has-many-table-div').offset().top - 100
}, 500);
}

return false;
});

$(document).on('pjax:beforeSend', function(e) {
var $targetForm = $(e.relatedTarget).closest('form');
if (!$targetForm.has('.has-many-table').length) return;

var fields = findHiddenRequired();
if (!fields.length) return;

e.preventDefault();
showAlert(fields);
return false;
});
}

$(init);
})(jQuery);
4 changes: 4 additions & 0 deletions resources/lang/en/exment.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@
],

'validation' => [
'hasmany_hidden_required_title' => 'Validation Error',
'hasmany_hidden_required_plain_prefix' => 'The following required fields are hidden:',
'hasmany_hidden_required_html_prefix' => 'The following required fields are hidden and must be shown:',
'hasmany_hidden_required_ok' => 'OK',
'current_password' => 'The current password is incorrect.',
'password_history' => 'The password is the same as the password registered in the past. Please enter another password.',
'complex_password' => 'The password must be at least 12 characters long and must contain three types of characters (uppercase letters, lowercase letters, numbers, symbols).',
Expand Down
4 changes: 4 additions & 0 deletions resources/lang/ja/exment.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@
],

'validation' => [
'hasmany_hidden_required_title' => 'バリデーションエラー',
'hasmany_hidden_required_plain_prefix' => '以下の必須項目が非表示になっています:',
'hasmany_hidden_required_html_prefix' => '以下の必須項目が非表示になっており、表示する必要があります:',
'hasmany_hidden_required_ok' => 'OK',
'current_password' => '現在のパスワードが正しくありません。',
'password_history' => '過去に登録したパスワードと同一のパスワードとなっています。他のパスワードを入力してください。',
'complex_password' => 'パスワードは12文字以上で、必ず3種類の文字種(英大文字、英小文字、数字、記号)を含む必要があります。',
Expand Down
5 changes: 5 additions & 0 deletions resources/lang_vendor/en/validation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

return [
'unique_in_table' => 'The :attribute has already been taken.',
];
1 change: 1 addition & 0 deletions resources/lang_vendor/ja/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
'string' => ':attributeには文字列を指定してください。',
'timezone' => ':attributeには正しい形式のタイムゾーンを指定してください。',
'unique' => 'その:attributeはすでに使われています。',
'unique_in_table' => 'その:attributeがすでに存在しています。',
'uploaded' => ':attributeのアップロードに失敗しました。',
'url' => ':attributeには正しい形式のURLを指定してください。',
'summary_condition' => 'この集計タイプは数値項目以外では指定できません。',
Expand Down
2 changes: 2 additions & 0 deletions resources/views/form/field/display.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
@endif
@endif
</span>
{{-- Hidden input to save value to database --}}
<input type="hidden" name="{{$name}}" value="{{$value}}" />
</div><!-- /.box-body -->
</div>

Expand Down
4 changes: 2 additions & 2 deletions src/ColumnItems/CustomItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,8 @@ protected function getCustomField($classname, $column_name_prefix = null)
if (!$this->hidden()) {
if ($this->initonly()) {
$field->displayText($this->html())->escape(false)->default($this->value)->prepareDefault();
} elseif ($this->viewonly() && !isset($this->value)) {
// if view only and create, set default value
} elseif ($this->viewonly() && is_null($this->id) && !isset($this->value)) {
// if view only and create (no id), set default value
$this->value = $this->getDefaultValue();
$field->displayText($this->html())->escape(false);
$this->value = null;
Expand Down
Loading
Loading