Merge remote-tracking branch 'origin/develop'
# Conflicts: # public/css/build/app.css # public/css/build/app.css.map # public/css/build/overrides.css # public/css/build/overrides.css.map # public/css/dist/all.css # public/mix-manifest.json
This commit is contained in:
@@ -1856,4 +1856,42 @@ class Helper
|
||||
return 'App\\Models\\'.ucwords($model);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a markdown-textarea value as HTML.
|
||||
*
|
||||
* Soft line breaks (single newlines) are rendered as <br> so that line
|
||||
* breaks typed in the textarea are preserved in the output.
|
||||
*
|
||||
* When $inline is true, block-level elements are suppressed and hard
|
||||
* breaks are pre-processed manually — used for the encrypted reveal span
|
||||
* where block HTML cannot be placed inside a font-size-toggled <span>.
|
||||
*/
|
||||
public static function renderMarkdown(?string $text, bool $inline = false): string
|
||||
{
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($inline) {
|
||||
// Convert newlines to CommonMark hard breaks for inline rendering
|
||||
$text = preg_replace('/(?<! {2})\n/', " \n", $text);
|
||||
|
||||
return Str::inlineMarkdown($text, ['html_input' => 'escape']);
|
||||
}
|
||||
|
||||
$html = trim(Str::markdown($text, [
|
||||
'html_input' => 'escape',
|
||||
'renderer' => ['soft_break' => "<br>\n"],
|
||||
]));
|
||||
|
||||
// If the entire output is a single <p> block, unwrap it so the content
|
||||
// renders inline-ish without the <p> adding unwanted top spacing in the
|
||||
// compact detail-view layout.
|
||||
if (str_starts_with($html, '<p>') && str_ends_with($html, '</p>') && substr_count($html, '<p>') === 1) {
|
||||
return substr($html, 3, -4);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ class CheckoutKitController extends Controller
|
||||
*/
|
||||
public function store(Request $request, $kit_id)
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
|
||||
$user_id = e($request->input('user_id'));
|
||||
if (is_null($user = User::find($user_id))) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_not_found'));
|
||||
|
||||
@@ -222,6 +222,10 @@ class ViewAssetsController extends Controller
|
||||
|
||||
return redirect()->back()->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
||||
} else {
|
||||
if ($fullItemType === Asset::class && is_null(Asset::RequestableAssets()->find($item->id))) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.requests.error'));
|
||||
}
|
||||
|
||||
$item->request();
|
||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
||||
$logaction->logaction('requested');
|
||||
|
||||
@@ -144,7 +144,7 @@ class AssetsTransformer
|
||||
|
||||
$fields_array[$field->name] = [
|
||||
'field' => e($field->db_column),
|
||||
'value' => e($value),
|
||||
'value' => ($field->element == 'markdown-textarea' && Gate::allows('assets.view.encrypted_custom_fields')) ? Helper::renderMarkdown($value) : e($value),
|
||||
'field_format' => $field->format,
|
||||
'element' => $field->element,
|
||||
];
|
||||
@@ -158,7 +158,7 @@ class AssetsTransformer
|
||||
|
||||
$fields_array[$field->name] = [
|
||||
'field' => e($field->db_column),
|
||||
'value' => e($value),
|
||||
'value' => ($field->element == 'markdown-textarea') ? Helper::renderMarkdown($value) : e($value),
|
||||
'field_format' => $field->format,
|
||||
'element' => $field->element,
|
||||
];
|
||||
@@ -274,7 +274,7 @@ class AssetsTransformer
|
||||
$value = Helper::getFormattedDateObject($value, 'date', false);
|
||||
}
|
||||
|
||||
$fields_array[$field->db_column] = e($value);
|
||||
$fields_array[$field->db_column] = ($field->element == 'markdown-textarea') ? Helper::renderMarkdown($value) : e($value);
|
||||
}
|
||||
|
||||
$array['custom_fields'] = $fields_array;
|
||||
|
||||
@@ -51,7 +51,7 @@ class CustomField extends Model
|
||||
*/
|
||||
protected $rules = [
|
||||
'name' => 'required|unique:custom_fields',
|
||||
'element' => 'required|in:text,listbox,textarea,checkbox,radio',
|
||||
'element' => 'required|in:text,listbox,textarea,markdown-textarea,checkbox,radio',
|
||||
'field_encrypted' => 'nullable|boolean',
|
||||
'auto_add_to_fieldsets' => 'boolean',
|
||||
'show_in_listview' => 'boolean',
|
||||
|
||||
@@ -7,11 +7,11 @@ use App\Models\PredefinedKit;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Class incapsulates checkout logic for reuse in different controllers
|
||||
*
|
||||
* @author [D. Minaev.] [<dmitriy.minaev.v@gmail.com>]
|
||||
*/
|
||||
class PredefinedKitCheckoutService
|
||||
@@ -19,9 +19,9 @@ class PredefinedKitCheckoutService
|
||||
use AuthorizesRequests;
|
||||
|
||||
/**
|
||||
* @param Request $request, this function works with fields: checkout_at, expected_checkin, note
|
||||
* @param PredefinedKit $kit kit for checkout
|
||||
* @param User $user checkout target
|
||||
* @param Request $request, this function works with fields: checkout_at, expected_checkin, note
|
||||
* @param PredefinedKit $kit kit for checkout
|
||||
* @param User $user checkout target
|
||||
* @return array Empty array if all ok, else [string_error1, string_error2...]
|
||||
*/
|
||||
public function checkout(Request $request, PredefinedKit $kit, User $user)
|
||||
@@ -93,7 +93,7 @@ class PredefinedKitCheckoutService
|
||||
}
|
||||
}
|
||||
if ($quantity > 0) {
|
||||
$errors[] = trans('admin/kits/general.none_models', ['model'=> $model->name, 'qty' => $model->pivot->quantity]);
|
||||
$errors[] = trans('admin/kits/general.none_models', ['model' => $model->name, 'qty' => $model->pivot->quantity]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +107,10 @@ class PredefinedKitCheckoutService
|
||||
->with('freeSeats')
|
||||
->get();
|
||||
foreach ($licenses as $license) {
|
||||
$this->authorize('checkout', $license);
|
||||
$quantity = $license->pivot->quantity;
|
||||
if ($quantity > count($license->freeSeats)) {
|
||||
$errors[] = trans('admin/kits/general.none_licenses', ['license'=> $license->name, 'qty' => $license->pivot->quantity]);
|
||||
$errors[] = trans('admin/kits/general.none_licenses', ['license' => $license->name, 'qty' => $license->pivot->quantity]);
|
||||
}
|
||||
for ($i = 0; $i < $quantity; $i++) {
|
||||
$seats_to_add[] = $license->freeSeats[$i];
|
||||
@@ -123,8 +124,9 @@ class PredefinedKitCheckoutService
|
||||
{
|
||||
$consumables = $kit->consumables()->with('users')->get();
|
||||
foreach ($consumables as $consumable) {
|
||||
$this->authorize('checkout', $consumable);
|
||||
if ($consumable->numRemaining() < $consumable->pivot->quantity) {
|
||||
$errors[] = trans('admin/kits/general.none_consumables', ['consumable'=> $consumable->name, 'qty' => $consumable->pivot->quantity]);
|
||||
$errors[] = trans('admin/kits/general.none_consumables', ['consumable' => $consumable->name, 'qty' => $consumable->pivot->quantity]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,8 +137,9 @@ class PredefinedKitCheckoutService
|
||||
{
|
||||
$accessories = $kit->accessories()->with('users')->get();
|
||||
foreach ($accessories as $accessory) {
|
||||
$this->authorize('checkout', $accessory);
|
||||
if ($accessory->numRemaining() < $accessory->pivot->quantity) {
|
||||
$errors[] = trans('admin/kits/general.none_accessory', ['accessory'=> $accessory->name, 'qty' => $accessory->pivot->quantity]);
|
||||
$errors[] = trans('admin/kits/general.none_accessory', ['accessory' => $accessory->name, 'qty' => $accessory->pivot->quantity]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ class PredefinedKitCheckoutService
|
||||
]);
|
||||
event(new CheckoutableCheckedOut($consumable, $user, $admin, $note));
|
||||
}
|
||||
//accessories
|
||||
// accessories
|
||||
foreach ($accessories_to_add as $accessory) {
|
||||
$accessory->assigned_to = $user->id;
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
|
||||
@@ -154,4 +154,15 @@ class CustomFieldFactory extends Factory
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function testMarkdownTextarea()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'name' => 'Notes',
|
||||
'help_text' => 'Additional notes about this asset. Markdown is supported.',
|
||||
'element' => 'markdown-textarea',
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class CustomFieldSeeder extends Seeder
|
||||
CustomField::factory()->count(1)->testEncrypted()->create();
|
||||
CustomField::factory()->count(1)->testCheckbox()->create();
|
||||
CustomField::factory()->count(1)->testRadio()->create();
|
||||
CustomField::factory()->count(1)->testMarkdownTextarea()->create();
|
||||
|
||||
DB::table('custom_field_custom_fieldset')->insert([
|
||||
[
|
||||
@@ -109,6 +110,20 @@ class CustomFieldSeeder extends Seeder
|
||||
'required' => 0,
|
||||
],
|
||||
|
||||
[
|
||||
'custom_field_id' => '9',
|
||||
'custom_fieldset_id' => '1',
|
||||
'order' => 0,
|
||||
'required' => 0,
|
||||
],
|
||||
|
||||
[
|
||||
'custom_field_id' => '9',
|
||||
'custom_fieldset_id' => '2',
|
||||
'order' => 0,
|
||||
'required' => 0,
|
||||
],
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=571ba00068523222c399e1ec11b89288",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=9bfab28a94932d45568ad50f3c6c5e2c",
|
||||
"/css/build/app.css": "/css/build/app.css?id=4b2abd7fa3560ada549e9d08bd836aa8",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=9f70623c0b56ef8ecec6fb859344d279",
|
||||
"/css/build/app.css": "/css/build/app.css?id=3cfdd1824004111aea48a50793be2518",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=bdf169bc2141f453390614c138cdce95",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=f5f404325dedd1abd00dc781664c0034",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=144c0b82011f8e02e3f71ba389755549",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
||||
|
||||
@@ -340,6 +340,12 @@ body {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
||||
// Clip the absolutely-positioned fixed-column overlays so they cannot
|
||||
// bleed past the container's bottom edge into the pagination area.
|
||||
.bootstrap-table .fixed-table-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
@page {
|
||||
@@ -695,21 +701,21 @@ h4 {
|
||||
|
||||
See https://github.com/grokability/snipe-it/issues/7989
|
||||
*/
|
||||
th.css-accessory > .th-inner,
|
||||
th.css-accessory-alt > .th-inner,
|
||||
th.css-barcode > .th-inner,
|
||||
th.css-component > .th-inner,
|
||||
th.css-consumable > .th-inner,
|
||||
th.css-envelope > .th-inner,
|
||||
th.css-house-flag > .th-inner,
|
||||
th.css-house-laptop > .th-inner,
|
||||
th.css-house-user > .th-inner,
|
||||
th.css-license > .th-inner,
|
||||
th.css-location > .th-inner,
|
||||
th.css-users > .th-inner,
|
||||
th.css-currency > .th-inner,
|
||||
th.css-child-locations > .th-inner,
|
||||
th.css-history > .th-inner
|
||||
thead th.css-accessory > .th-inner,
|
||||
thead th.css-accessory-alt > .th-inner,
|
||||
thead th.css-barcode > .th-inner,
|
||||
thead th.css-component > .th-inner,
|
||||
thead th.css-consumable > .th-inner,
|
||||
thead th.css-envelope > .th-inner,
|
||||
thead th.css-house-flag > .th-inner,
|
||||
thead th.css-house-laptop > .th-inner,
|
||||
thead th.css-house-user > .th-inner,
|
||||
thead th.css-license > .th-inner,
|
||||
thead th.css-location > .th-inner,
|
||||
thead th.css-users > .th-inner,
|
||||
thead th.css-currency > .th-inner,
|
||||
thead th.css-child-locations > .th-inner,
|
||||
thead th.css-history > .th-inner
|
||||
{
|
||||
font-size: 0px;
|
||||
line-height: 0.75 !important;
|
||||
@@ -720,22 +726,22 @@ th.css-history > .th-inner
|
||||
}
|
||||
|
||||
|
||||
th.css-location > .th-inner::before,
|
||||
th.css-accessory > .th-inner::before,
|
||||
th.css-accessory-alt > .th-inner::before,
|
||||
th.css-barcode > .th-inner::before,
|
||||
th.css-component > .th-inner::before,
|
||||
th.css-consumable > .th-inner::before,
|
||||
th.css-envelope > .th-inner::before,
|
||||
th.css-house-flag > .th-inner::before,
|
||||
th.css-house-laptop > .th-inner::before,
|
||||
th.css-house-user > .th-inner::before,
|
||||
th.css-license > .th-inner::before,
|
||||
th.css-location > .th-inner::before,
|
||||
th.css-users > .th-inner::before,
|
||||
th.css-currency > .th-inner::before,
|
||||
th.css-child-locations > .th-inner::before,
|
||||
th.css-history > .th-inner::before
|
||||
thead th.css-location > .th-inner::before,
|
||||
thead th.css-accessory > .th-inner::before,
|
||||
thead th.css-accessory-alt > .th-inner::before,
|
||||
thead th.css-barcode > .th-inner::before,
|
||||
thead th.css-component > .th-inner::before,
|
||||
thead th.css-consumable > .th-inner::before,
|
||||
thead th.css-envelope > .th-inner::before,
|
||||
thead th.css-house-flag > .th-inner::before,
|
||||
thead th.css-house-laptop > .th-inner::before,
|
||||
thead th.css-house-user > .th-inner::before,
|
||||
thead th.css-license > .th-inner::before,
|
||||
thead th.css-location > .th-inner::before,
|
||||
thead th.css-users > .th-inner::before,
|
||||
thead th.css-currency > .th-inner::before,
|
||||
thead th.css-child-locations > .th-inner::before,
|
||||
thead th.css-history > .th-inner::before
|
||||
{
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
@@ -747,91 +753,91 @@ th.css-history > .th-inner::before
|
||||
BEGIN ICON TABLE HEADERS
|
||||
Set the font-weight css property as 900 (For Solid), 400 (Regular or Brands), 300 (Light for pro icons).
|
||||
**/
|
||||
th.css-barcode > .th-inner::before
|
||||
thead th.css-barcode > .th-inner::before
|
||||
{
|
||||
content: "\f02a"; font-family: "Font Awesome 5 Free"; font-weight: 900;
|
||||
}
|
||||
|
||||
th.css-license > .th-inner::before
|
||||
thead th.css-license > .th-inner::before
|
||||
{
|
||||
content: "\f0c7"; font-family: "Font Awesome 5 Free"; font-weight: 400;
|
||||
}
|
||||
|
||||
th.css-consumable > .th-inner::before
|
||||
thead th.css-consumable > .th-inner::before
|
||||
{
|
||||
content: "\f043"; font-family: "Font Awesome 5 Free"; font-weight: 900;
|
||||
}
|
||||
|
||||
th.css-envelope > .th-inner::before
|
||||
thead th.css-envelope > .th-inner::before
|
||||
{
|
||||
content: "\f0e0"; font-family: "Font Awesome 5 Free"; font-weight: 400;
|
||||
}
|
||||
|
||||
th.css-accessory > .th-inner::before
|
||||
thead th.css-accessory > .th-inner::before
|
||||
{
|
||||
content: "\f11c"; font-family: "Font Awesome 5 Free"; font-weight: 400;
|
||||
}
|
||||
|
||||
th.css-users > .th-inner::before {
|
||||
thead th.css-users > .th-inner::before {
|
||||
content: "\f0c0"; font-family: "Font Awesome 5 Free"; font-size: 15px;
|
||||
}
|
||||
|
||||
th.css-location > .th-inner::before {
|
||||
thead th.css-location > .th-inner::before {
|
||||
content: "\f3c5"; font-family: "Font Awesome 5 Free"; font-size: 19px; margin-bottom: 0px;
|
||||
}
|
||||
|
||||
th.css-component > .th-inner::before
|
||||
thead th.css-component > .th-inner::before
|
||||
{
|
||||
content: "\f0a0"; font-family: "Font Awesome 5 Free"; font-weight: 500;
|
||||
}
|
||||
|
||||
th.css-padlock > .th-inner::before
|
||||
thead th.css-padlock > .th-inner::before
|
||||
{
|
||||
content: "\f023"; font-family: "Font Awesome 5 Free";
|
||||
font-weight: 800;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
th.css-house-user > .th-inner::before {
|
||||
thead th.css-house-user > .th-inner::before {
|
||||
content: "\e1b0";
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
th.css-house-flag > .th-inner::before {
|
||||
thead th.css-house-flag > .th-inner::before {
|
||||
content: "\e50d";
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
th.css-house-laptop > .th-inner::before {
|
||||
thead th.css-house-laptop > .th-inner::before {
|
||||
content: "\e066";
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
th.css-accessory-alt > .th-inner::before {
|
||||
thead th.css-accessory-alt > .th-inner::before {
|
||||
content: "\f11c";
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
th.css-child-locations > .th-inner::before {
|
||||
thead th.css-child-locations > .th-inner::before {
|
||||
content: "\f64f"; // change this to f51e for coins
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
th.css-currency > .th-inner::before {
|
||||
thead th.css-currency > .th-inner::before {
|
||||
content: "\24"; // change this to f51e for coins
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
th.css-history > .th-inner::before {
|
||||
thead th.css-history > .th-inner::before {
|
||||
content: "\f1da"; // change this to f51e for coins
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: 19px;
|
||||
@@ -1186,6 +1192,19 @@ caption.tableCaption {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
|
||||
.markdown-field-content {
|
||||
h1 { font-size: 22px; font-weight: 700; margin: 6px 0 4px; }
|
||||
h2 { font-size: 20px; font-weight: 700; margin: 6px 0 4px; }
|
||||
h3 { font-size: 18px; font-weight: 700; margin: 6px 0 4px; }
|
||||
h4 { font-size: 16px; font-weight: 700; margin: 4px 0 4px; }
|
||||
h5, h6 { font-size: 14px; font-weight: 700; margin: 4px 0 4px; }
|
||||
p { margin: 0 0 4px; }
|
||||
p:last-child { margin-bottom: 0; }
|
||||
ul, ol { margin: 0 0 4px; padding-left: 20px; }
|
||||
> *:first-child { margin-top: 0; }
|
||||
}
|
||||
|
||||
// via https://github.com/grokability/snipe-it/issues/11754
|
||||
.sidebar-toggle.btn {
|
||||
border-radius: 3px;
|
||||
@@ -1307,6 +1326,7 @@ Radio toggle styles for permission settings and check/uncheck all
|
||||
}
|
||||
|
||||
.js-copy-link {
|
||||
float: left;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,10 @@ return [
|
||||
'text' => 'Text Box',
|
||||
'listbox' => 'List Box',
|
||||
'textarea' => 'Textarea (multi-line)',
|
||||
'markdown-textarea' => 'Markdown Textarea',
|
||||
'checkbox' => 'Checkbox',
|
||||
'radio' => 'Radio Buttons',
|
||||
],
|
||||
'markdown_supported' => 'Markdown is supported',
|
||||
'general_help_text' => 'Custom fields store additional information not covered by the default asset fields. <a href="https://snipe-it.readme.io/docs/custom-fields#/"><i class="fa fa-external-link"></i></a>.',
|
||||
];
|
||||
|
||||
@@ -8,42 +8,48 @@
|
||||
</x-copy-to-clipboard>
|
||||
{{-- Hidden span used as copy target --}}
|
||||
{{-- It's tempting to break out the HTML into separate lines for this, but it results in extra spaces being added onto the end of the copied value --}}
|
||||
{{-- For markdown fields, position: absolute removes it from normal flow so it can't create an anonymous block box --}}
|
||||
@if (($field->field_encrypted=='1') && (Gate::allows('assets.view.encrypted_custom_fields')))
|
||||
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0px;">{{ ($field->isFieldDecryptable($item->{$field->db_column_name()}) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $item->{$field->db_column_name()}) }}</span>
|
||||
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0;{{ $field->element === 'markdown-textarea' ? ' position: absolute;' : '' }}">{{ ($field->isFieldDecryptable($item->{$field->db_column_name()}) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $item->{$field->db_column_name()}) }}</span>
|
||||
@elseif (($field->field_encrypted=='1') && (Gate::denies('assets.view.encrypted_custom_fields')))
|
||||
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0px;">{{ strtoupper(trans('admin/custom_fields/general.encrypted')) }}</span>
|
||||
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0;{{ $field->element === 'markdown-textarea' ? ' position: absolute;' : '' }}">{{ strtoupper(trans('admin/custom_fields/general.encrypted')) }}</span>
|
||||
@else
|
||||
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0px;">{{ $item->{$field->db_column_name()} }}</span>
|
||||
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0;{{ $field->element === 'markdown-textarea' ? ' position: absolute;' : '' }}">{{ $item->{$field->db_column_name()} }}</span>
|
||||
@endif
|
||||
|
||||
@endif
|
||||
|
||||
@if (($field->field_encrypted=='1') && ($item->{$field->db_column_name()}!='') && (Gate::allows('assets.view.encrypted_custom_fields')))
|
||||
<i class="fas fa-lock" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}" onclick="showHideEncValue(this)" id="text-{{ $field->id }}"></i>
|
||||
<i class="fas fa-lock fa-fw pull-right" style="font-size: 16px;" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}" onclick="showHideEncValue(this)" id="text-{{ $field->id }}"></i>
|
||||
@endif
|
||||
|
||||
@if ($field->isFieldDecryptable($item->{$field->db_column_name()} ))
|
||||
@can('assets.view.encrypted_custom_fields')
|
||||
@php
|
||||
$fieldSize = strlen(Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}))
|
||||
$fieldSize = strlen(\App\Helpers\Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}))
|
||||
@endphp
|
||||
@if ($fieldSize > 0)
|
||||
<span id="text-{{ $field->id }}-to-hide">***********</span>
|
||||
<span id="text-{{ $field->id }}-to-hide" style="font-size: 20px;vertical-align:middle;">***********</span>
|
||||
@if (($field->format=='URL') && ($item->{$field->db_column_name()}!=''))
|
||||
<span class="js-copy-{{ $field->id }} hidden-print"
|
||||
id="text-{{ $field->id }}-to-show"
|
||||
style="font-size: 0px;">
|
||||
<a href="{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}"
|
||||
target="_new">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</a>
|
||||
</span>
|
||||
style="font-size: 0;">
|
||||
<a href="{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}"
|
||||
target="_new">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</a>
|
||||
</span>
|
||||
@elseif (($field->format=='DATE') && ($item->{$field->db_column_name()}!=''))
|
||||
<span class="js-copy-{{ $field->id }} hidden-print"
|
||||
id="text-{{ $field->id }}-to-show"
|
||||
style="font-size: 0px;">{{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false)) }}</span>
|
||||
style="font-size: 0;">{{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false)) }}</span>
|
||||
@elseif ($field->element == 'markdown-textarea')
|
||||
<div class="js-copy-{{ $field->id }} hidden-print markdown-field-content"
|
||||
id="text-{{ $field->id }}-to-show"
|
||||
data-markdown="true"
|
||||
style="display: none;">{!! Helper::renderMarkdown(Helper::gracefulDecrypt($field, $item->{$field->db_column_name()})) !!}</div>
|
||||
@else
|
||||
<span class="js-copy-{{ $field->id }} hidden-print"
|
||||
id="text-{{ $field->id }}-to-show"
|
||||
style="font-size: 0px;">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</span>
|
||||
style="font-size: 0;">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</span>
|
||||
@endif
|
||||
@endif
|
||||
@else
|
||||
@@ -57,6 +63,8 @@
|
||||
<a href="{{ $item->{$field->db_column_name()} }}" target="_new">{{ $item->{$field->db_column_name()} }}</a>
|
||||
@elseif (($field->format=='DATE') && ($item->{$field->db_column_name()}!=''))
|
||||
{{ \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false) }}
|
||||
@elseif (($field->element == 'markdown-textarea') && ($item->{$field->db_column_name()} != ''))
|
||||
<div class="markdown-field-content">{!! Helper::renderMarkdown($item->{$field->db_column_name()}) !!}</div>
|
||||
@else
|
||||
{!! nl2br(e($item->{$field->db_column_name()})) !!}
|
||||
@endif
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
'text' => trans('admin/custom_fields/general.types.text'),
|
||||
'listbox' => trans('admin/custom_fields/general.types.listbox'),
|
||||
'textarea' => trans('admin/custom_fields/general.types.textarea'),
|
||||
'markdown-textarea' => trans('admin/custom_fields/general.types.markdown-textarea'),
|
||||
'checkbox' => trans('admin/custom_fields/general.types.checkbox'),
|
||||
'radio' => trans('admin/custom_fields/general.types.radio'),
|
||||
]"
|
||||
@@ -347,7 +348,7 @@
|
||||
// and don't display encryption option for checkbox or radio
|
||||
$(".field_element").change(function(){
|
||||
$(this).find("option:selected").each(function(){
|
||||
if (($(this).attr("value")!="text") && ($(this).attr("value")!="textarea")){
|
||||
if (($(this).attr("value") != "text") && ($(this).attr("value") != "textarea") && ($(this).attr("value") != "markdown-textarea")) {
|
||||
$("#field_values_text").show();
|
||||
if ($(this).attr("value") == "checkbox" || $(this).attr("value") == "radio") {
|
||||
$("#encryption_section").hide();
|
||||
|
||||
@@ -2377,6 +2377,8 @@
|
||||
// Use element id to find the text element to hide / show
|
||||
var targetElement = e.id+"-to-show";
|
||||
var hiddenElement = e.id+"-to-hide";
|
||||
var targetEl = document.getElementById(targetElement);
|
||||
var isMarkdown = targetEl && targetEl.dataset.markdown;
|
||||
var audio = new Audio('{{ config('app.url') }}/sounds/lock.mp3');
|
||||
if($(e).hasClass('fa-lock')) {
|
||||
@if ((isset($user)) && ($user->enable_sounds))
|
||||
@@ -2384,7 +2386,11 @@
|
||||
@endif
|
||||
$(e).removeClass('fa-lock').addClass('fa-unlock');
|
||||
// Show the encrypted custom value and hide the element with asterisks
|
||||
document.getElementById(targetElement).style.fontSize = "100%";
|
||||
if (isMarkdown) {
|
||||
targetEl.style.display = "block";
|
||||
} else {
|
||||
targetEl.style.fontSize = "100%";
|
||||
}
|
||||
document.getElementById(hiddenElement).style.display = "none";
|
||||
|
||||
} else {
|
||||
@@ -2393,7 +2399,12 @@
|
||||
@endif
|
||||
$(e).removeClass('fa-unlock').addClass('fa-lock');
|
||||
// ClipboardJS can't copy display:none elements so use a trick to hide the value
|
||||
document.getElementById(targetElement).style.fontSize = "0px";
|
||||
if (isMarkdown) {
|
||||
targetEl.style.display = "none";
|
||||
} else {
|
||||
// ClipboardJS can't copy display:none elements so use a trick to hide the value
|
||||
targetEl.style.fontSize = "0px";
|
||||
}
|
||||
document.getElementById(hiddenElement).style.display = "";
|
||||
|
||||
}
|
||||
@@ -2515,23 +2526,34 @@
|
||||
|
||||
// Function to add original value to elements
|
||||
function addValue($element) {
|
||||
// Get original value of the element
|
||||
var originalValue = $element.text().trim();
|
||||
var originalHtml = $element.html().trim();
|
||||
var originalText = $element.text().trim();
|
||||
var hasHtmlContent = originalHtml !== '' && originalHtml !== originalText;
|
||||
|
||||
// Show asterisks only for not empty values
|
||||
if (originalValue !== '') {
|
||||
// This is necessary to avoid loop because value is generated dynamically
|
||||
if (originalValue !== '' && originalValue !== asterisks) $element.attr('value', originalValue);
|
||||
// Show asterisks only for non-empty values
|
||||
if (originalText !== '') {
|
||||
var asterisks = '*'.repeat(11);
|
||||
// Avoid reprocessing already-asterisked elements
|
||||
if (originalText !== asterisks) {
|
||||
if (hasHtmlContent) {
|
||||
$element.data('encrypted-html', originalHtml);
|
||||
}
|
||||
$element.attr('value', originalText);
|
||||
}
|
||||
|
||||
// Hide the original value and show asterisks of the same length
|
||||
var asterisks = '*'.repeat(originalValue.length);
|
||||
// Hide the original value and show a fixed-length asterisk placeholder
|
||||
$element.text(asterisks);
|
||||
|
||||
// Add click event to show original text
|
||||
// Add click event to show original value
|
||||
$element.click(function() {
|
||||
var $this = $(this);
|
||||
if ($this.text().trim() === asterisks) {
|
||||
$this.text($this.attr('value'));
|
||||
var savedHtml = $this.data('encrypted-html');
|
||||
if (savedHtml) {
|
||||
$this.html(savedHtml);
|
||||
} else {
|
||||
$this.text($this.attr('value'));
|
||||
}
|
||||
} else {
|
||||
$this.text(asterisks);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
|
||||
|
||||
<label for="{{ $field->db_column_name() }}" class="col-md-3 control-label">
|
||||
|
||||
@if ($field->field_encrypted)
|
||||
<i class="fas fa-lock" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}"></i>
|
||||
@endif
|
||||
|
||||
{{ $field->name }}
|
||||
|
||||
</label>
|
||||
|
||||
<div class="col-md-7 col-sm-12">
|
||||
<div class="col-md-8 col-sm-12">
|
||||
|
||||
@if ($field->element!='text')
|
||||
|
||||
@@ -36,7 +41,14 @@
|
||||
|
||||
@elseif ($field->element=='textarea')
|
||||
<!-- Textarea -->
|
||||
<textarea class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
|
||||
<textarea rows="6" class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
|
||||
|
||||
@elseif ($field->element=='markdown-textarea')
|
||||
<!-- Markdown Textarea -->
|
||||
<textarea rows="6" class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
|
||||
<p class="help-block">
|
||||
<i class="fab fa-markdown" aria-hidden="true"></i> {{ trans('admin/custom_fields/general.markdown_supported') }}
|
||||
</p>
|
||||
|
||||
@elseif ($field->element=='checkbox')
|
||||
<!-- Checkbox -->
|
||||
@@ -108,11 +120,6 @@
|
||||
?>
|
||||
</div>
|
||||
|
||||
@if ($field->field_encrypted)
|
||||
<div class="col-md-1 col-sm-1 text-left">
|
||||
<i class="fas fa-lock" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}"></i>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -496,7 +496,9 @@
|
||||
if (cell.is('th')) {
|
||||
return cell.find('.th-inner').text()
|
||||
}
|
||||
return htmlData
|
||||
// Convert <br> tags to newlines so that line breaks in notes and
|
||||
// textarea fields survive HTML-stripping during export
|
||||
return htmlData.replace(/<br\s*\/?>/gi, '\n');
|
||||
}
|
||||
|
||||
// This allows us to override the table defaults set below using the data-dash attributes
|
||||
@@ -1993,6 +1995,13 @@
|
||||
return '<a href="mailto:' + row.custom_fields[field_column_plain].value + '" style="white-space: nowrap" data-tooltip="true" title="{{ trans('general.send_email') }}"><x-icon type="email" /> ' + row.custom_fields[field_column_plain].value + '</a>';
|
||||
}
|
||||
}
|
||||
// Convert newlines to <br> for textarea fields so they render in
|
||||
// the table; export will convert <br> back to \n via onCellHtmlData
|
||||
if (row.custom_fields[field_column_plain].element === 'textarea') {
|
||||
var val = row.custom_fields[field_column_plain].value;
|
||||
return val ? val.replace(/(?:\r\n|\r|\n)/g, '<br>') : '';
|
||||
}
|
||||
|
||||
return row.custom_fields[field_column_plain].value;
|
||||
|
||||
}
|
||||
|
||||
@@ -381,7 +381,8 @@
|
||||
@endif
|
||||
<p class="help-block">
|
||||
{{ trans('admin/settings/general.dashboard_message_help') }}
|
||||
{!! trans('general.github_markdown') !!}</p>
|
||||
<i class="fab fa-markdown" aria-hidden="true"> {!! trans('general.github_markdown') !!}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
Reference in New Issue
Block a user