Changeset 3262394
- Timestamp:
- 03/26/2025 07:07:00 PM (11 months ago)
- Location:
- customtables/trunk
- Files:
-
- 21 edited
-
CustomTables.php (modified) (3 diffs)
-
libraries/customtables/ct/Environment.php (modified) (1 diff)
-
libraries/customtables/filter/filtering.php (modified) (4 diffs)
-
libraries/customtables/html/inputbox.php (modified) (1 diff)
-
libraries/customtables/html/inputbox/multilingualstring.php (modified) (1 diff)
-
libraries/customtables/html/inputbox/signature.php (modified) (1 diff)
-
libraries/customtables/html/searchbox/date.php (modified) (2 diffs)
-
libraries/customtables/html/searchbox/tablejoin.php (modified) (1 diff)
-
libraries/customtables/html/searchinputbox.php (modified) (1 diff)
-
libraries/customtables/html/toolbar.php (modified) (6 diffs)
-
libraries/customtables/layouts/Twig_HTML_Tags.php (modified) (1 diff)
-
libraries/customtables/layouts/twig.php (modified) (1 diff)
-
libraries/customtables/media/js/base64.js (modified) (1 diff)
-
libraries/customtables/media/js/catalog.js (modified) (21 diffs)
-
libraries/customtables/media/js/edit.js (modified) (23 diffs)
-
libraries/customtables/media/js/extratasks.js (modified) (1 diff)
-
libraries/customtables/media/js/layoutwizard.js (modified) (1 diff)
-
libraries/customtables/media/js/modal.js (modified) (1 diff)
-
libraries/customtables/media/js/uploader.js (modified) (1 diff)
-
libraries/customtables/media/xml/tags.xml (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
customtables/trunk/CustomTables.php
r3259510 r3262394 4 4 Plugin URI: https://ct4.us 5 5 Description: Custom Tables solution for WordPress 6 Version: 1.5. 76 Version: 1.5.8 7 7 Requires at least: 6.0 8 8 Requires PHP: 7.4.0 … … 32 32 33 33 define(CTWP . 'PLUGIN_NAME', 'customtables'); 34 define(CTWP . 'PLUGIN_VERSION', '1.5. 7');34 define(CTWP . 'PLUGIN_VERSION', '1.5.8'); 35 35 define(CTWP . 'PLUGIN_NAME_DIR', plugin_dir_path(__FILE__)); 36 36 define(CTWP . 'PLUGIN_NAME_URL', plugin_dir_url(__FILE__)); … … 116 116 function enqueue_codemirror() 117 117 { 118 $version = '1.5. 7';118 $version = '1.5.8'; 119 119 wp_enqueue_style('customtables-js-modal', plugin_dir_url(__FILE__) . 'libraries/customtables/media/css/modal.css', false, $version); 120 120 wp_enqueue_style('customtables-js-layouteditor', plugin_dir_url(__FILE__) . 'libraries/customtables/media/css/layouteditor.css', false, $version); -
customtables/trunk/libraries/customtables/ct/Environment.php
r3244178 r3262394 32 32 var bool $clean; 33 33 var string $frmt; 34 var string $WebsiteRoot; 34 var string $WebsiteRoot;//With trailing front slash / 35 35 var bool $advancedTagProcessor; 36 36 var bool $isMobile; -
customtables/trunk/libraries/customtables/filter/filtering.php
r3259497 r3262394 919 919 920 920 if ($valueStart and $valueEnd) { 921 //Breadcrumbs 922 $this->PathValue[] = $title1 . ' ' 923 . esc_html__("from", "customtables") . ' ' . $titleStart . ' ' 924 . esc_html__("to", "customtables") . ' ' . $titleEnd; 925 926 $whereClause->addCondition($fieldRow1['realfieldname'], $valueStart, '>='); 927 $whereClause->addCondition($fieldRow1['realfieldname'], $valueEnd, '<='); 921 922 if ($valueStart == $valueEnd) { 923 //Breadcrumbs 924 $this->PathValue[] = $title1 . ' ' 925 . esc_html__("Date", "customtables") . ' ' . $titleStart; 926 927 $whereClause->addCondition($fieldRow1['realfieldname'], $valueStart . ' 00:00:00', '>='); 928 $whereClause->addCondition($fieldRow1['realfieldname'], $valueEnd . ' 23:59:59', '<='); 929 930 } else { 931 //Breadcrumbs 932 $this->PathValue[] = $title1 . ' ' 933 . esc_html__("from", "customtables") . ' ' . $titleStart . ' ' 934 . esc_html__("to", "customtables") . ' ' . $titleEnd; 935 936 $whereClause->addCondition($fieldRow1['realfieldname'], $valueStart . ' 00:00:00', '>='); 937 $whereClause->addCondition($fieldRow1['realfieldname'], $valueEnd . ' 23:59:59', '<='); 938 } 939 928 940 } elseif ($valueStart and $valueEnd === null) { 929 941 $this->PathValue[] = $title1 . ' ' 930 942 . esc_html__("From", "customtables") . ' ' . $titleStart; 931 943 932 $whereClause->addCondition($fieldRow1['realfieldname'] , $valueStart, '>=');944 $whereClause->addCondition($fieldRow1['realfieldname'] . ' 00:00:00', $valueStart, '>='); 933 945 } elseif ($valueStart === null and $valueEnd) { 934 946 $this->PathValue[] = $title1 . ' ' 935 947 . esc_html__("To", "customtables") . ' ' . $valueEnd; 936 948 937 $whereClause->addCondition($fieldRow1['realfieldname'], $valueEnd, '<='); 938 } 949 $whereClause->addCondition($fieldRow1['realfieldname'], $valueEnd . ' 23:59:59', '<='); 950 } 951 939 952 return $whereClause; 940 953 } … … 974 987 $value2 = $value; 975 988 $title2 = $fieldRow2['fieldtitle' . $this->ct->Languages->Postfix]; 989 $value2isFieldName = true; 976 990 } else { 977 991 $value2 = $value; 978 992 $title2 = $value; 993 $value2isFieldName = false; 979 994 } 980 995 … … 987 1002 elseif ($value2 == 'NULL' and $comparison_operator == '!=') 988 1003 $whereClause->addCondition($value1, null, 'NOT NULL'); 989 else 990 $whereClause->addCondition($value1, $value2, $comparison_operator); 1004 else { 1005 if ($value2isFieldName or ($comparison_operator != '=' and $comparison_operator != '==')) { 1006 $whereClause->addCondition($value1, $value2, $comparison_operator); 1007 } else { 1008 $whereClause->addCondition($value1, $value2 . ' 00:00:00', '>='); 1009 $whereClause->addCondition($value1, $value2 . ' 23:59:59', '<='); 1010 } 1011 } 1012 991 1013 return $whereClause; 992 1014 } … … 1139 1161 $rows = database::loadAssocList($ct->Table->realtablename, $selects, $whereClause, $fieldRow['realfieldname']); 1140 1162 1141 $result .= '<select id="' . $control_name . 'SQLJoinLink" class="' . common::convertClassString('form-select') . '" onchange=" ctInputbox_UpdateSQLJoinLink(\'' . $control_name . '\',\'' . $control_name_postfix . '\')">';1163 $result .= '<select id="' . $control_name . 'SQLJoinLink" class="' . common::convertClassString('form-select') . '" onchange="CTEditHelper.ctInputbox_UpdateSQLJoinLink(\'' . $control_name . '\',\'' . $control_name_postfix . '\')">'; 1142 1164 $result .= '<option value="">- ' . esc_html__("Select", "customtables") . '</option>'; 1143 1165 -
customtables/trunk/libraries/customtables/html/inputbox.php
r3256163 r3262394 561 561 $onchange .= ';'; 562 562 } 563 if (substr($attributes['onchange'], -1) !== ';') { 563 564 if (!empty($attributes['onchange']) and substr($attributes['onchange'], -1) !== ';') { 564 565 $attributes['onchange'] .= ';'; 565 566 } -
customtables/trunk/libraries/customtables/html/inputbox/multilingualstring.php
r3228624 r3262394 89 89 if (str_contains(($this->attributes['onchange'] ?? ''), 'ct_UpdateSingleValue(')) { 90 90 91 $attributes['onchange'] = "ct_UpdateSingleValue('" . $this->ct->Env->WebsiteRoot . "',"91 $attributes['onchange'] = 'ct_UpdateSingleValue(' 92 92 . $this->ct->Params->ItemId . ",'" . $this->field->fieldname . $postfix . "'," 93 93 . "'" . $this->row[$this->ct->Table->realidfieldname] . "'," -
customtables/trunk/libraries/customtables/html/inputbox/signature.php
r3228624 r3262394 58 58 $result .= ' 59 59 <script> 60 ctInputbox_signature("' . $ctInputbox_signature . '")60 CTEditHelper.ctInputbox_signature("' . $ctInputbox_signature . '") 61 61 </script>'; 62 62 return $result; -
customtables/trunk/libraries/customtables/html/searchbox/date.php
r3228624 r3262394 29 29 common::loadJQueryUI(); 30 30 31 $js = ' 31 if (empty($value)) { 32 $result = $this->ct->Filter->whereClause->getWhereParamValue($this->ct->Table->fieldPrefix . 'CreatedAt'); 33 if ($result['value'] !== null) 34 $value = explode(' ', $result['value'])[0]; // Extracts only the date part 35 36 } 37 38 $matchType = $this->attributes['data-match']; 39 40 $valueParts = explode('-to-', $value); 41 42 $valueStart = isset($valueParts[0]) ? trim($valueParts[0]) : ''; 43 44 // Sanitize and validate date format 45 $dateFormat = 'Y-m-d'; // Adjust the format according to your needs 46 47 if ($valueStart) { 48 $startDateTime = DateTime::createFromFormat($dateFormat, $valueStart); 49 50 if ($startDateTime !== false) { 51 $valueStart = $startDateTime->format($dateFormat); 52 } else { 53 // Invalid date format, handle the error or set a default value 54 $valueStart = ''; // Set to default or perform error handling 55 } 56 } 57 58 if ($matchType == 'exact') { 59 60 $js = ' 61 62 jQuery(document).ready(function($) { 63 $("#' . $this->objectName . '_exact").datepicker({ 64 dateFormat: "yy-mm-dd", 65 onSelect: function(selectedDate) { 66 //$("#' . $this->objectName . '").datepicker("option", "minDate", selectedDate); 67 } 68 }); 69 }); 70 71 '; 72 73 if (isset($this->ct->LayoutVariables['script'])) 74 $this->ct->LayoutVariables['script'] .= $js; 75 else 76 $this->ct->LayoutVariables['script'] = $js; 77 78 $hidden = '<input type="hidden" name="' . $this->objectName . '" id="' . $this->objectName . '" value="' . $valueStart . '">'; 79 $jsOnChange = 'ctSearchBarDateUpdate(\'' . $this->field->fieldname . '\',function () {' . $this->attributes['onchange'] . '});'; 80 81 $start = '<input onblur="' . $jsOnChange . '" onchange="' . $jsOnChange . '" value="' . $valueStart . '" type="text"' 82 . ' class="' . ($this->attributes['class'] ?? '') . '" id="' . $this->objectName . '_exact"' 83 . ' placeholder="' . $this->field->title . ' - ' . esc_html__("Date", "customtables") . '"' 84 . ' style="display:inline-block;width:49%;margin-left:0;margin-right:0;float:left;">'; 85 86 return $hidden . '<div style="position: relative;">' . $start . '</div>'; 87 } else { 88 89 $js = ' 32 90 33 91 jQuery(document).ready(function($) { … … 49 107 '; 50 108 51 $this->ct->LayoutVariables['script'] .= $js;109 $this->ct->LayoutVariables['script'] .= $js; 52 110 53 $valueParts = explode('-to-', $value); 111 $valueEnd = isset($valueParts[1]) ? trim($valueParts[1]) : ''; 112 if ($valueEnd) { 113 $endDateTime = DateTime::createFromFormat($dateFormat, $valueEnd); 54 114 55 $valueStart = isset($valueParts[0]) ? trim($valueParts[0]) : ''; 56 $valueEnd = isset($valueParts[1]) ? trim($valueParts[1]) : ''; 115 if ($endDateTime !== false) { 116 $valueEnd = $endDateTime->format($dateFormat); 117 } else { 118 // Invalid date format, handle the error or set a default value 119 $valueEnd = ''; // Set to default or perform error handling 120 } 121 } 57 122 58 // Sanitize and validate date format59 $dateFormat = 'Y-m-d'; // Adjust the format according to your needs123 $hidden = '<input type="hidden" name="' . $this->objectName . '" id="' . $this->objectName . '" value="' . $valueStart . '-to-' . $valueEnd . '">'; 124 $jsOnChange = 'ctSearchBarDateRangeUpdate(\'' . $this->field->fieldname . '\')'; 60 125 61 if ($valueStart) { 62 $startDateTime = DateTime::createFromFormat($dateFormat, $valueStart); 126 $start = '<input onblur="' . $jsOnChange . '" onchange="' . $jsOnChange . '" value="' . $valueStart . '" type="text"' 127 . ' class="' . ($this->attributes['class'] ?? '') . '" id="' . $this->objectName . '_start"' 128 . ' placeholder="' . $this->field->title . ' - ' . esc_html__("Start", "customtables") . '"' 129 . ' style="display:inline-block;width:49%;margin-left:0;margin-right:0;float:left;">'; 63 130 64 if ($startDateTime !== false) {65 $valueStart = $startDateTime->format($dateFormat);66 } else {67 // Invalid date format, handle the error or set a default value68 $valueStart = ''; // Set to default or perform error handling 69 }131 $end = '<input onblur="' . $jsOnChange . '" onchange="' . $jsOnChange . '" value="' . $valueEnd . '" type="text"' 132 . ' class="' . ($this->attributes['class'] ?? '') . '" id="' . $this->objectName . '_end"' 133 . ' placeholder="' . $this->field->title . ' - ' . esc_html__("End", "customtables") . '"' 134 . ' style="display:inline-block;width:49%;margin-left:0;margin-right:0;float:right;">'; 135 136 return $hidden . '<div style="position: relative;">' . $start . $end . '</div>'; 70 137 } 71 72 if ($valueEnd) {73 $endDateTime = DateTime::createFromFormat($dateFormat, $valueEnd);74 75 if ($endDateTime !== false) {76 $valueEnd = $endDateTime->format($dateFormat);77 } else {78 // Invalid date format, handle the error or set a default value79 $valueEnd = ''; // Set to default or perform error handling80 }81 }82 83 $jsOnChange = 'ctSearchBarDateRangeUpdate(\'' . $this->field->fieldname . '\')';84 85 $hidden = '<input type="hidden" name="' . $this->objectName . '" id="' . $this->objectName . '" value="' . $valueStart . '-to-' . $valueEnd . '">';86 87 $start = '<input onblur="' . $jsOnChange . '" onchange="' . $jsOnChange . '" value="' . $valueStart . '" type="text"'88 . ' class="' . ($this->attributes['class'] ?? '') . '" id="' . $this->objectName . '_start"'89 . ' placeholder="' . $this->field->title . ' - ' . esc_html__("Start", "customtables") . '"'90 . ' style="display:inline-block;width:49%;margin-left:0;margin-right:0;float:left;">';91 92 $end = '<input onblur="' . $jsOnChange . '" onchange="' . $jsOnChange . '" value="' . $valueEnd . '" type="text"'93 . ' class="' . ($this->attributes['class'] ?? '') . '" id="' . $this->objectName . '_end"'94 . ' placeholder="' . $this->field->title . ' - ' . esc_html__("End", "customtables") . '"'95 . ' style="display:inline-block;width:49%;margin-left:0;margin-right:0;float:right;">';96 97 return $hidden . '<div style="position: relative;">' . $start . $end . '</div>';98 138 } 99 139 } -
customtables/trunk/libraries/customtables/html/searchbox/tablejoin.php
r3247356 r3262394 349 349 if (typeof ctInputbox_removeEmptyParents === "function") { 350 350 ctInputbox_removeEmptyParents("' . $control_name . '",""); 351 ctInputbox_UpdateSQLJoinLink("' . $control_name . '","");351 CTEditHelper.ctInputbox_UpdateSQLJoinLink("' . $control_name . '",""); 352 352 '; 353 353 if (str_contains(($this->attributes['class'] ?? ''), ' ct_virtualselect_selectbox')) -
customtables/trunk/libraries/customtables/html/searchinputbox.php
r3248691 r3262394 105 105 'url' => 'string', 106 106 'virtual' => 'string', 107 'email' => 'string' 107 'email' => 'string', 108 'creationtime' => 'date', 109 'changetime' => 'date', 110 'lastviewtime' => 'date' 108 111 ]; 109 112 -
customtables/trunk/libraries/customtables/html/toolbar.php
r3255615 r3262394 56 56 57 57 case 'refresh': 58 $rid = ' esRefreshIcon' . $this->rid;58 $rid = 'ctRefreshIcon' . $this->rid; 59 59 $icon = Icons::iconRefresh($this->ct->Env->toolbarIcons); 60 60 $moduleIDString = $this->ct->Params->ModuleId === null ? 'null' : $this->ct->Params->ModuleId; … … 134 134 $a = '<a href="' . $link . '">' . $icon . '</a>'; 135 135 136 return '<div id=" esEditIcon' . $this->rid . '" class="toolbarIcons">' . $a . '</div>';136 return '<div id="ctEditIcon' . $this->rid . '" class="toolbarIcons">' . $a . '</div>'; 137 137 } 138 138 … … 250 250 } 251 251 if ($min_ordering_field !== null) { 252 $fieldTitleValue = $this->getFieldCleanValue4RDI($min_ordering_field); 253 return substr($fieldTitleValue, -100); 252 253 //$fieldRow = $this->ct->Table->getFieldByName() 254 255 $valueProcessor = new Value($this->ct); 256 $fieldTitleValue = $valueProcessor->renderValue($min_ordering_field, $this->ct->Table->record, [], true); 257 258 //$fieldTitleValue = $this->getFieldCleanValue4RDI($min_ordering_field); 259 return $fieldTitleValue;//substr($fieldTitleValue, -100); 254 260 } 255 261 return null; 256 }257 258 protected function getFieldCleanValue4RDI($mFld): string259 {260 $titleField = $mFld['realfieldname'];261 if (str_contains($mFld['type'], 'multi'))262 $titleField .= $this->ct->Languages->Postfix;263 264 $fieldTitleValue = $this->row[$titleField];265 $deleteLabel = common::ctStripTags($fieldTitleValue ?? '');266 267 $deleteLabel = trim(preg_replace("/[^a-zA-Z\d ,.]/", "", $deleteLabel));268 return preg_replace('/\s{3,}/', ' ', $deleteLabel);269 262 } 270 263 … … 286 279 { 287 280 $deleteLabel = $this->firstFieldValueLabel(); 288 $icon = Icons::iconDelete($this->ct->Env->toolbarIcons);289 $message = 'Do you want to delete (' . $deleteLabel . ')?';290 281 $moduleIDString = $this->ct->Params->ModuleId === null ? 'null' : $this->ct->Params->ModuleId; 291 $href = 'javascript:ctDeleteRecord(\'' . $message . '\', ' . $this->Table->tableid . ', \'' . $this->listing_id . '\', \'esDeleteIcon' . $this->rid . '\', ' . $moduleIDString . ');'; 292 return '<div id="esDeleteIcon' . $this->rid . '" class="toolbarIcons"><a href="' . $href . '">' . $icon . '</a></div>'; 282 283 $href = 'javascript:ctDeleteRecord(' . $this->Table->tableid . ', \'' . $this->listing_id . '\', ' . $moduleIDString . ');'; 284 285 $messageDiv = '<div id="ctDeleteMessage' . $this->rid . '" style="display:none;">Do you want to delete ' . $deleteLabel . '?</div>'; 286 $a = '<a href="' . $href . '">' . Icons::iconDelete($this->ct->Env->toolbarIcons) . '</a>'; 287 $result = '<div id="ctDeleteIcon' . $this->rid . '" class="toolbarIcons">' . $messageDiv . $a . '</div>';; 288 289 return $result; 293 290 } 294 291 … … 296 293 { 297 294 if ($this->isPublishable) { 298 $rid = ' esPublishIcon' . $this->rid;295 $rid = 'ctPublishIcon' . $this->rid; 299 296 300 297 $moduleIDString = $this->ct->Params->ModuleId === null ? 'null' : $this->ct->Params->ModuleId; … … 314 311 return ''; 315 312 } 313 314 protected function getFieldCleanValue4RDI($mFld): string 315 { 316 $titleField = $mFld['realfieldname']; 317 if (str_contains($mFld['type'], 'multi')) 318 $titleField .= $this->ct->Languages->Postfix; 319 320 $fieldTitleValue = $this->row[$titleField]; 321 $deleteLabel = common::ctStripTags($fieldTitleValue ?? ''); 322 323 $deleteLabel = trim(preg_replace("/[^a-zA-Z\d ,.]/", "", $deleteLabel)); 324 return preg_replace('/\s{3,}/', ' ', $deleteLabel); 325 } 316 326 } -
customtables/trunk/libraries/customtables/layouts/Twig_HTML_Tags.php
r3255615 r3262394 1000 1000 $onclick = 'setTask(event, "' . $task . '","' . $redirect . '",true,"' . $formName . '",' . $isModal . ',null,' . ($this->ct->Params->ModuleId === null ? 'null' : $this->ct->Params->ModuleId) . ');'; 1001 1001 else 1002 $onclick = 'setTask(event, "' . $task . '","' . $redirect . '",true,"' . $formName . '",' . $isModal . ',"' . $parentField . '" ' . ($this->ct->Params->ModuleId === null ? 'null' : $this->ct->Params->ModuleId) . ');';1002 $onclick = 'setTask(event, "' . $task . '","' . $redirect . '",true,"' . $formName . '",' . $isModal . ',"' . $parentField . '",' . ($this->ct->Params->ModuleId === null ? 'null' : $this->ct->Params->ModuleId) . ');'; 1003 1003 1004 1004 return '<input id="' . $buttonId . '" type="submit" class="' . common::convertClassString($the_class) . '"' . $attribute . ' onClick=\'' . $onclick . '\' value="' . $title . '">'; -
customtables/trunk/libraries/customtables/layouts/twig.php
r3248691 r3262394 631 631 632 632 $listOfRecords = implode(',', $this->ct->Table->recordlist); 633 $onchange = 'ct_UpdateAllRecordsValues( \'' . $this->ct->Env->WebsiteRoot . '\',' . $this->ct->Params->ItemId . ',\''633 $onchange = 'ct_UpdateAllRecordsValues(' . $this->ct->Params->ItemId . ',\'' 634 634 . $this->field->fieldname . '\',\'' . $listOfRecords . '\',\'' 635 635 . $postfix . '\',' . ($this->ct->Params->ModuleId ?? 0) . ');'; 636 636 } else { 637 $onchange = 'ct_UpdateSingleValue( \'' . $this->ct->Env->WebsiteRoot . '\',' . $this->ct->Params->ItemId . ',\''637 $onchange = 'ct_UpdateSingleValue(' . $this->ct->Params->ItemId . ',\'' 638 638 . $this->field->fieldname . '\',\'' . $this->ct->Table->record[$this->ct->Table->realidfieldname] . '\',\'' 639 639 . $postfix . '\',' . ($this->ct->Params->ModuleId ?? 0) . ');'; -
customtables/trunk/libraries/customtables/media/js/base64.js
r3202701 r3262394 5 5 * 6 6 **/ 7 const Base64 = {8 7 9 // private property 10 //_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 11 _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 8 if (typeof globalThis.CustomTablesEdit === 'undefined') { 12 9 13 // public method for encoding 14 encode: function (input) { 15 let output = ""; 16 let chr1, chr2, chr3, enc1, enc2, enc3, enc4; 17 let i = 0; 10 const Base64 = { 18 11 19 input = Base64._utf8_encode(input); 12 // private property 13 //_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 14 _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 20 15 21 while (i < input.length) { 16 // public method for encoding 17 encode: function (input) { 18 let output = ""; 19 let chr1, chr2, chr3, enc1, enc2, enc3, enc4; 20 let i = 0; 22 21 23 chr1 = input.charCodeAt(i++); 24 chr2 = input.charCodeAt(i++); 25 chr3 = input.charCodeAt(i++); 22 input = Base64._utf8_encode(input); 26 23 27 enc1 = chr1 >> 2; 28 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 29 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 30 enc4 = chr3 & 63; 24 while (i < input.length) { 31 25 32 if (isNaN(chr2)) { 33 enc3 = enc4 = 64; 34 } else if (isNaN(chr3)) { 35 enc4 = 64; 26 chr1 = input.charCodeAt(i++); 27 chr2 = input.charCodeAt(i++); 28 chr3 = input.charCodeAt(i++); 29 30 enc1 = chr1 >> 2; 31 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 32 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 33 enc4 = chr3 & 63; 34 35 if (isNaN(chr2)) { 36 enc3 = enc4 = 64; 37 } else if (isNaN(chr3)) { 38 enc4 = 64; 39 } 40 41 output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 42 36 43 } 37 44 38 output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);45 let r = 4 - (output.length % 4); 39 46 47 const a = []; 48 while (a.length < r) { 49 a.push('='); 50 } 51 let s = a.join(''); 52 53 return output + s; 54 }, 55 56 57 // public method for decoding 58 decode: function (input) { 59 let output = ""; 60 let chr1, chr2, chr3; 61 let enc1, enc2, enc3, enc4; 62 let i = 0; 63 64 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 65 66 while (i < input.length) { 67 68 enc1 = this._keyStr.indexOf(input.charAt(i++)); 69 enc2 = this._keyStr.indexOf(input.charAt(i++)); 70 enc3 = this._keyStr.indexOf(input.charAt(i++)); 71 enc4 = this._keyStr.indexOf(input.charAt(i++)); 72 73 chr1 = (enc1 << 2) | (enc2 >> 4); 74 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 75 chr3 = ((enc3 & 3) << 6) | enc4; 76 77 output = output + String.fromCharCode(chr1); 78 79 if (enc3 !== 64) { 80 output = output + String.fromCharCode(chr2); 81 } 82 if (enc4 !== 64) { 83 output = output + String.fromCharCode(chr3); 84 } 85 86 } 87 88 output = Base64._utf8_decode(output); 89 90 return output; 91 92 }, 93 94 // private method for UTF-8 encoding 95 _utf8_encode: function (string) { 96 string = string.replace(/\r\n/g, "\n"); 97 let utftext = ""; 98 99 for (let n = 0; n < string.length; n++) { 100 101 const c = string.charCodeAt(n); 102 103 if (c < 128) { 104 utftext += String.fromCharCode(c); 105 } else if ((c > 127) && (c < 2048)) { 106 utftext += String.fromCharCode((c >> 6) | 192); 107 utftext += String.fromCharCode((c & 63) | 128); 108 } else { 109 utftext += String.fromCharCode((c >> 12) | 224); 110 utftext += String.fromCharCode(((c >> 6) & 63) | 128); 111 utftext += String.fromCharCode((c & 63) | 128); 112 } 113 114 } 115 116 return utftext; 117 }, 118 119 // private method for UTF-8 decoding 120 _utf8_decode: function (utftext) { 121 let string = ""; 122 let i = 0; 123 let c1; 124 let c2; 125 let c3; 126 let c = c1 = c2 = 0; 127 128 while (i < utftext.length) { 129 130 c = utftext.charCodeAt(i); 131 132 if (c < 128) { 133 string += String.fromCharCode(c); 134 i++; 135 } else if ((c > 191) && (c < 224)) { 136 c2 = utftext.charCodeAt(i + 1); 137 string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 138 i += 2; 139 } else { 140 c2 = utftext.charCodeAt(i + 1); 141 c3 = utftext.charCodeAt(i + 2); 142 string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 143 i += 3; 144 } 145 146 } 147 148 return string; 40 149 } 41 150 42 let r = 4 - (output.length % 4);151 }; 43 152 44 const a = []; 45 while (a.length < r) { 46 a.push('='); 47 } 48 let s = a.join(''); 49 50 return output + s; 51 }, 52 53 54 // public method for decoding 55 decode: function (input) { 56 let output = ""; 57 let chr1, chr2, chr3; 58 let enc1, enc2, enc3, enc4; 59 let i = 0; 60 61 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 62 63 while (i < input.length) { 64 65 enc1 = this._keyStr.indexOf(input.charAt(i++)); 66 enc2 = this._keyStr.indexOf(input.charAt(i++)); 67 enc3 = this._keyStr.indexOf(input.charAt(i++)); 68 enc4 = this._keyStr.indexOf(input.charAt(i++)); 69 70 chr1 = (enc1 << 2) | (enc2 >> 4); 71 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 72 chr3 = ((enc3 & 3) << 6) | enc4; 73 74 output = output + String.fromCharCode(chr1); 75 76 if (enc3 !== 64) { 77 output = output + String.fromCharCode(chr2); 78 } 79 if (enc4 !== 64) { 80 output = output + String.fromCharCode(chr3); 81 } 82 83 } 84 85 output = Base64._utf8_decode(output); 86 87 return output; 88 89 }, 90 91 // private method for UTF-8 encoding 92 _utf8_encode: function (string) { 93 string = string.replace(/\r\n/g, "\n"); 94 let utftext = ""; 95 96 for (let n = 0; n < string.length; n++) { 97 98 const c = string.charCodeAt(n); 99 100 if (c < 128) { 101 utftext += String.fromCharCode(c); 102 } else if ((c > 127) && (c < 2048)) { 103 utftext += String.fromCharCode((c >> 6) | 192); 104 utftext += String.fromCharCode((c & 63) | 128); 105 } else { 106 utftext += String.fromCharCode((c >> 12) | 224); 107 utftext += String.fromCharCode(((c >> 6) & 63) | 128); 108 utftext += String.fromCharCode((c & 63) | 128); 109 } 110 111 } 112 113 return utftext; 114 }, 115 116 // private method for UTF-8 decoding 117 _utf8_decode: function (utftext) { 118 let string = ""; 119 let i = 0; 120 let c1; 121 let c2; 122 let c3; 123 let c = c1 = c2 = 0; 124 125 while (i < utftext.length) { 126 127 c = utftext.charCodeAt(i); 128 129 if (c < 128) { 130 string += String.fromCharCode(c); 131 i++; 132 } else if ((c > 191) && (c < 224)) { 133 c2 = utftext.charCodeAt(i + 1); 134 string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 135 i += 2; 136 } else { 137 c2 = utftext.charCodeAt(i + 1); 138 c3 = utftext.charCodeAt(i + 2); 139 string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 140 i += 3; 141 } 142 143 } 144 145 return string; 146 } 147 148 }; 153 globalThis.Base64 = Base64; // Store globally 154 } -
customtables/trunk/libraries/customtables/media/js/catalog.js
r3256163 r3262394 8 8 **/ 9 9 10 let ctLinkLoading = false;11 12 10 function ctCreateUser(msg, listing_id, toolbarBoxId, ModuleId) { 13 11 if (confirm(msg)) { … … 72 70 73 71 function esEditObject(objId, toolbarBoxId, Itemid, tmpl, returnto) { 74 if ( ctLinkLoading) return;75 76 ctLinkLoading = true;72 if (CTEditHelper.ctLinkLoading) return; 73 74 CTEditHelper.ctLinkLoading = true; 77 75 document.getElementById(toolbarBoxId).innerHTML = ''; 78 76 79 77 let return_to = btoa(window.location.href); 80 let link = ctWebsiteRoot + 'index.php?option=com_customtables&view=edititem&listing_id=' + objId + '&Itemid=' + Itemid + '&returnto=' + return_to;78 let link = CTEditHelper.websiteRoot + 'index.php?option=com_customtables&view=edititem&listing_id=' + objId + '&Itemid=' + Itemid + '&returnto=' + return_to; 81 79 82 80 if (tmpl !== '') link += '&tmpl=' + tmpl; … … 99 97 100 98 let http = CreateHTTPRequestObject(); // defined in ajax.js 101 102 99 let addParams = ['clean=1']; 103 100 let url = esPrepareLink(['task', "listing_id", 'returnto', 'ids'], addParams); 104 105 console.warn(url);106 101 107 102 if (http) { … … 111 106 112 107 if (http.readyState === 4) { 108 113 109 let res = http.response.replace(/(\r\n|\n|\r)/gm, ""); 114 110 115 111 if (responses.indexOf(res) !== -1) { 116 112 117 let element_tableid_tr = "ctTable_" + tableid + '_' + recordId; 118 119 let table_object = document.getElementById("ctTable_" + tableid); 113 let table_object = findTableByRowId(tableid + '_' + recordId); 120 114 if (!reload && table_object && CTEditHelper.cmsName === 'Joomla') { 121 let index = findRowIndexById("ctTable_" + tableid, element_tableid_tr); 122 if (task === 'delete') 115 116 if (task === 'delete') { 117 let index = findRowIndexById(table_object, tableid, recordId, "ctDeleteIcon"); 123 118 table_object.deleteRow(index); 124 else 119 } else { 120 121 let icon = 'ctEditIcon'; 122 123 if (task === 'copy') { 124 window.location.reload(); 125 return; 126 } else if (task === 'refresh') 127 icon = 'ctRefreshIcon'; 128 else if (task === 'publish' || task === 'unpublish') 129 icon = 'ctPublishIcon'; 130 131 let index = findRowIndexById(table_object, tableid, recordId, icon); 125 132 ctCatalogUpdate(tableid, recordId, index, ModuleId); 133 } 126 134 } else { 127 135 window.location.reload(); 128 136 } 129 137 130 ctLinkLoading = false;138 CTEditHelper.ctLinkLoading = false; 131 139 132 140 if (last) { … … 144 152 145 153 function ctRefreshRecord(tableid, recordId, toolbarBoxId, ModuleId) { 146 if ( ctLinkLoading) return;147 ctLinkLoading = true;154 if (CTEditHelper.ctLinkLoading) return; 155 CTEditHelper.ctLinkLoading = true; 148 156 runTheTask('refresh', tableid, recordId, ['refreshed'], false, false, ModuleId); 149 157 } 150 158 151 159 function ctCopyRecord(tableid, listing_id, toolbarBoxId, ModuleId) { 152 if ( ctLinkLoading) return;153 154 ctLinkLoading = true;160 if (CTEditHelper.ctLinkLoading) return; 161 162 CTEditHelper.ctLinkLoading = true; 155 163 156 164 if (document.getElementById(toolbarBoxId)) … … 207 215 208 216 function ctPublishRecord(tableid, recordId, toolbarBoxId, publish, ModuleId) { 209 if ( ctLinkLoading) return;210 211 ctLinkLoading = true;217 if (CTEditHelper.ctLinkLoading) return; 218 219 CTEditHelper.ctLinkLoading = true; 212 220 document.getElementById(toolbarBoxId).innerHTML = ''; 213 221 runTheTask((publish === 0 ? 'unpublish' : 'publish'), tableid, recordId, ['published', 'unpublished'], false, false, ModuleId); 214 222 } 215 223 216 function findRowIndexById(tableid, rowId) { 217 218 let table_object = document.getElementById(tableid); 219 220 if (table_object) { 221 let rows = table_object.rows; 222 for (let i = 0; i < rows.length; i++) { 223 if (rows.item(i).id === rowId) return i; 224 } 225 } 224 function findTableByRowId(rowId) { 225 let row = document.getElementById(`ctTable_${rowId}`); 226 return row ? row.closest("table") : null; 227 } 228 229 function findRowIndexById(table, tableid, id, icon) { 230 231 //icon = "ctDeleteIcon" 232 if (!table) return -2; 233 let lookingFor = '#' + icon + tableid + "x" + id; 234 console.warn("lookingFor", lookingFor) 235 let rows = table.rows; 236 console.log("count:", rows.length) 237 for (let i = 0; i < rows.length; i++) { 238 239 let deleteIcon = rows[i].querySelector(lookingFor); 240 if (deleteIcon) { 241 return i; 242 } 243 } 244 226 245 return -1; 227 246 } 228 247 229 function ctDeleteRecord(msg, tableid, recordId, toolbarBoxId, ModuleId) { 230 if (ctLinkLoading) return; 231 232 ctLinkLoading = true; 233 234 if (confirm(msg)) { 235 runTheTask('delete', tableid, recordId, ['deleted'], false, false, ModuleId); 236 } else { 237 ctLinkLoading = false; 248 function ctDeleteRecord(tableid, recordId, ModuleId) { 249 if (CTEditHelper.ctLinkLoading) return; 250 251 CTEditHelper.ctLinkLoading = true; 252 253 let msgObj = document.getElementById('ctDeleteMessage' + tableid + 'x' + recordId); 254 if (msgObj) { 255 // Strip HTML tags and sanitize the message 256 let msg = msgObj.textContent || msgObj.innerText || ""; 257 258 if (confirm(msg)) { 259 runTheTask('delete', tableid, recordId, ['deleted'], false, false, ModuleId); 260 } else { 261 CTEditHelper.ctLinkLoading = false; 262 } 238 263 } 239 264 } … … 245 270 246 271 function ctSearchBoxDo() { 247 if ( ctLinkLoading) return;248 249 ctLinkLoading = true;272 if (CTEditHelper.ctLinkLoading) return; 273 274 CTEditHelper.ctLinkLoading = true; 250 275 let w = []; 251 276 let allSearchElements = document.querySelectorAll('[ctSearchBoxField]'); … … 265 290 if (objValue.length < l) { 266 291 alert(obj.dataset.label + ": " + TranslateText('COM_CUSTOMTABLES_SEARCH_ALERT_MINLENGTH', l)); 267 ctLinkLoading = false;292 CTEditHelper.ctLinkLoading = false; 268 293 return; 269 294 } … … 304 329 305 330 function ctSearchReset() { 306 if ( ctLinkLoading) return;307 308 ctLinkLoading = true;331 if (CTEditHelper.ctLinkLoading) return; 332 333 CTEditHelper.ctLinkLoading = true; 309 334 310 335 window.location.href = esPrepareLink(['where', 'task', "listing_id", 'returnto'], []); … … 347 372 function ctToolBarDO(task, tableid, ModuleId) { 348 373 349 if ( ctLinkLoading) return;350 351 ctLinkLoading = true;374 if (CTEditHelper.ctLinkLoading) return; 375 376 CTEditHelper.ctLinkLoading = true; 352 377 const elements = getListOfSelectedRecords(tableid); 353 378 354 379 if (elements.length === 0) { 355 380 alert(TranslateText('COM_CUSTOMTABLES_JS_SELECT_RECORDS')); 356 ctLinkLoading = false;381 CTEditHelper.ctLinkLoading = false; 357 382 return; 358 383 } … … 364 389 365 390 if (!confirm(msg)) { 366 ctLinkLoading = false;391 CTEditHelper.ctLinkLoading = false; 367 392 return; 368 393 } … … 406 431 } 407 432 408 function ct_UpdateAllRecordsValues( WebsiteRoot,Itemid, fieldname_, record_ids, postfix, ModuleId) {433 function ct_UpdateAllRecordsValues(Itemid, fieldname_, record_ids, postfix, ModuleId) { 409 434 let ids = record_ids.split(','); 410 435 const obj_checkbox_off = document.getElementById(ctFieldInputPrefix + "_" + fieldname_ + "_off"); … … 418 443 document.getElementById(objectName).checked = parseInt(obj_checkbox_off.value) === 1; 419 444 420 ct_UpdateSingleValue( WebsiteRoot,Itemid, fieldname_, ids[i], postfix, ModuleId);445 ct_UpdateSingleValue(Itemid, fieldname_, ids[i], postfix, ModuleId); 421 446 } 422 447 … … 429 454 let obj = document.getElementById(objectName); 430 455 obj.value = value; 431 ct_UpdateSingleValue( WebsiteRoot,Itemid, fieldname_, ids[i], postfix, ModuleId);456 ct_UpdateSingleValue(Itemid, fieldname_, ids[i], postfix, ModuleId); 432 457 if (obj.dataset.type === "sqljoin") { 433 458 434 459 let tableid = obj.dataset.tableid; 435 let table_object = document.getElementById("ctTable_" + tableid); 460 //let table_object = document.getElementById("ctTable_" + tableid); 461 let table_object = findTableByRowId(tableid + '_' + ids[i]); 436 462 437 463 if (table_object) { 438 let element_tableid_tr = "ctTable_" + tableid + '_' + ids[i]; 439 let index = findRowIndexById("ctTable_" + tableid, element_tableid_tr); 464 let index = findRowIndexById(table_object, tableid, ids[i], 'ctEditIcon'); 440 465 ctCatalogUpdate(tableid, ids[i], index, ModuleId); 441 466 } … … 445 470 } 446 471 447 function ct_UpdateSingleValue( WebsiteRoot,Itemid, fieldname_, record_id, postfix, ModuleId) {472 function ct_UpdateSingleValue(Itemid, fieldname_, record_id, postfix, ModuleId) { 448 473 449 474 let params = ""; … … 464 489 params += "&" + ctFieldInputPrefix + fieldname_ + "=" + document.getElementById(objectName).value; 465 490 } 466 ct_UpdateSingleValueSet( WebsiteRoot,Itemid, fieldname_, record_id, postfix, ModuleId, params);467 } 468 469 function ct_UpdateSingleValueSet( WebsiteRoot,Itemid, fieldname_, record_id, postfix, ModuleId, valueParam) {491 ct_UpdateSingleValueSet(Itemid, fieldname_, record_id, postfix, ModuleId, params); 492 } 493 494 function ct_UpdateSingleValueSet(Itemid, fieldname_, record_id, postfix, ModuleId, valueParam) { 470 495 471 496 const fieldname = fieldname_.split('_')[0]; … … 537 562 } 538 563 539 function ctCatalogUpdate(tableid, recordsId, row_index, ModuleId) {540 541 let element_tableid = "ctTable_" + tableid;564 function ctCatalogUpdate(tableid, listing_id, row_index, ModuleId) { 565 566 //let element_tableid = "ctTable_" + tableid; 542 567 543 568 let deleteParams = ['task', "listing_id", 'returnto', 'ids', 'option', 'view', 'clean', 'component', 'frmt']; 544 let addParams = ['listing_id=' + recordsId, 'number=' + row_index, 'clean=1'];569 let addParams = ['listing_id=' + listing_id, 'number=' + row_index, 'clean=1']; 545 570 546 571 if (CTEditHelper.cmsName === 'Joomla') { … … 566 591 if (http.readyState === 4) { 567 592 let res = http.response; 568 let tableObj = document.getElementById(element_tableid); 593 594 //let tableObj = document.getElementById(element_tableid); 595 let tableObj = findTableByRowId(tableid + '_' + listing_id); 596 569 597 if (tableObj) { 570 598 let rows = tableObj.rows; … … 614 642 let element_tableid_tr = "ctTable_" + to_parts[1] + '_' + to_parts[2]; 615 643 616 let table_object = document.getElementById("ctTable_" + to_parts[1]); 644 //let table_object = document.getElementById("ctTable_" + to_parts[1]); 645 let table_object = findTableByRowId(to_parts[1] + '_' + to_parts[2]); 617 646 618 647 let index; 619 if (table_object) index = findRowIndexById( "ctTable_" + to_parts[1], element_tableid_tr);648 if (table_object) index = findRowIndexById(table_object, to_parts[1], to_parts[2], 'ctEditIcon'); 620 649 621 650 let deleteParams = ['task', "listing_id", 'returnto', 'ids', 'option', 'view', 'clean', 'component', 'frmt']; … … 678 707 let res = http.response; 679 708 680 ctShowPopUp(res, true); 709 if (res.indexOf('view-login') !== -1) { 710 alert('Session expired. Please login again.'); 711 location.reload(); 712 return; 713 } else { 714 ctShowPopUp(res, true); 715 } 681 716 682 717 //Activate Calendars if found … … 735 770 }, 300) 736 771 } 772 773 function ctSearchBarDateUpdate(fieldName, callback) { 774 setTimeout(function () { 775 let obj = document.getElementById(ctFieldInputPrefix + "search_box_" + fieldName); 776 777 // Store the previous value in dataset 778 let v = document.getElementById(ctFieldInputPrefix + "search_box_" + fieldName + "_exact").value; 779 780 if (obj.value !== v) { 781 782 obj.value = v; 783 784 // Execute callback if provided 785 if (typeof callback === "function") { 786 callback(); 787 } 788 } 789 }, 300); 790 } -
customtables/trunk/libraries/customtables/media/js/edit.js
r3256443 r3262394 7 7 * @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html 8 8 **/ 9 class CustomTablesEdit { 10 11 constructor(cmsName = 'Joomla', cmsVersion = 5, itemId = 0) { 12 this.GoogleDriveTokenClient = []; 13 this.GoogleDriveAccessToken = null; 14 this.cmsName = cmsName; 15 this.cmsVersion = cmsVersion; 16 this.itemId = itemId; 17 } 18 19 GoogleDriveInitClient(fieldName, GoogleDriveAPIKey, GoogleDriveClientId) { 20 this.GoogleDriveTokenClient[fieldName] = google.accounts.oauth2.initTokenClient({ 21 client_id: GoogleDriveClientId, 22 scope: "https://www.googleapis.com/auth/drive.readonly", 23 callback: (tokenResponse) => { 24 if (tokenResponse && tokenResponse.access_token) { 25 this.GoogleDriveAccessToken = tokenResponse.access_token; 26 CTEditHelper.GoogleDriveLoadPicker(fieldName, GoogleDriveAPIKey, tokenResponse.access_token); 27 } 28 }, 29 }); 30 } 31 32 GoogleDriveLoadPicker(fieldName, GoogleDriveAPIKey, access_token) { 33 gapi.load("picker", { 34 callback: function () { 35 CTEditHelper.GoogleDriveCreatePicker(fieldName, GoogleDriveAPIKey, access_token); 36 } 37 }); 38 } 39 40 GoogleDriveCreatePicker(fieldName, GoogleDriveAPIKey, access_token) { 41 if (access_token) { 42 const pickerBuilder = new google.picker.PickerBuilder() 43 .addView(google.picker.ViewId.DOCS) 44 .addView(google.picker.ViewId.FOLDERS) 45 .addView(new google.picker.DocsView(google.picker.ViewId.DOCS) 46 .setIncludeFolders(true) 47 .setOwnedByMe(false) 48 .setLabel("Shared with me")) 49 .setOAuthToken(access_token) 50 .setDeveloperKey(GoogleDriveAPIKey) 51 .setCallback(function (data) { 52 CTEditHelper.GoogleDrivePickerCallback(fieldName, data, access_token) 9 10 if (typeof globalThis.CustomTablesEdit === 'undefined') { 11 class CustomTablesEdit { 12 13 //Always used as "CTEditHelper" 14 constructor(cmsName = 'Joomla', cmsVersion = 5, itemId = null, websiteRoot = null) { 15 this.GoogleDriveTokenClient = []; 16 this.GoogleDriveAccessToken = null; 17 this.cmsName = cmsName; 18 this.cmsVersion = cmsVersion; 19 this.itemId = itemId; 20 21 this.ct_signaturePad_fields = []; 22 this.ct_signaturePad = []; 23 this.ct_signaturePad_formats = []; 24 25 this.ctInputBoxRecords_dynamic_filter = []; 26 27 this.ctLinkLoading = false; 28 29 this.websiteRoot = websiteRoot;//With trailing front slash / 30 } 31 32 GoogleDriveInitClient(fieldName, GoogleDriveAPIKey, GoogleDriveClientId) { 33 this.GoogleDriveTokenClient[fieldName] = google.accounts.oauth2.initTokenClient({ 34 client_id: GoogleDriveClientId, 35 scope: "https://www.googleapis.com/auth/drive.readonly", 36 callback: (tokenResponse) => { 37 if (tokenResponse && tokenResponse.access_token) { 38 this.GoogleDriveAccessToken = tokenResponse.access_token; 39 CTEditHelper.GoogleDriveLoadPicker(fieldName, GoogleDriveAPIKey, tokenResponse.access_token); 40 } 41 }, 42 }); 43 } 44 45 GoogleDriveLoadPicker(fieldName, GoogleDriveAPIKey, access_token) { 46 gapi.load("picker", { 47 callback: function () { 48 CTEditHelper.GoogleDriveCreatePicker(fieldName, GoogleDriveAPIKey, access_token); 49 } 50 }); 51 } 52 53 GoogleDriveCreatePicker(fieldName, GoogleDriveAPIKey, access_token) { 54 if (access_token) { 55 const pickerBuilder = new google.picker.PickerBuilder() 56 .addView(google.picker.ViewId.DOCS) 57 .addView(google.picker.ViewId.FOLDERS) 58 .addView(new google.picker.DocsView(google.picker.ViewId.DOCS) 59 .setIncludeFolders(true) 60 .setOwnedByMe(false) 61 .setLabel("Shared with me")) 62 .setOAuthToken(access_token) 63 .setDeveloperKey(GoogleDriveAPIKey) 64 .setCallback(function (data) { 65 CTEditHelper.GoogleDrivePickerCallback(fieldName, data, access_token) 66 }); 67 68 if (google.picker.ViewId.SHARED_DRIVES) { 69 pickerBuilder.addView(google.picker.ViewId.SHARED_DRIVES); 70 } 71 72 const picker = pickerBuilder.build(); 73 picker.setVisible(true); 74 } 75 } 76 77 GoogleDrivePickerCallback(fieldName, data, access_token) { 78 79 if (data[google.picker.Response.ACTION] === google.picker.Action.PICKED) { 80 if (data[google.picker.Response.DOCUMENTS] && data[google.picker.Response.DOCUMENTS].length > 0) { 81 const file = data[google.picker.Response.DOCUMENTS][0]; 82 CTEditHelper.GoogleDriveGetFileMetadata(fieldName, file.id, access_token); 83 } else { 84 console.log("No file was selected or the response format has changed."); 85 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = "No file was selected."; 86 } 87 } else if (data[google.picker.Response.ACTION] === google.picker.Action.CANCEL) { 88 console.log("User closed the Picker or canceled selection."); 89 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = "File selection was canceled."; 90 } 91 } 92 93 formatFileSize(bytes) { 94 if (bytes === 0) return '0 Bytes'; 95 const k = 1024; 96 const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 97 const i = Math.floor(Math.log(bytes) / Math.log(k)); 98 return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; 99 } 100 101 emptyContainers(boxId, className) { 102 const parentElement = document.getElementById(boxId); 103 104 if (parentElement) { 105 const containers = parentElement.getElementsByClassName(className); 106 107 Array.from(containers).forEach(container => { 108 container.innerHTML = ''; 53 109 }); 54 55 if (google.picker.ViewId.SHARED_DRIVES) { 56 pickerBuilder.addView(google.picker.ViewId.SHARED_DRIVES); 57 } 58 59 const picker = pickerBuilder.build(); 60 picker.setVisible(true); 61 } 62 } 63 64 GoogleDrivePickerCallback(fieldName, data, access_token) { 65 66 if (data[google.picker.Response.ACTION] === google.picker.Action.PICKED) { 67 if (data[google.picker.Response.DOCUMENTS] && data[google.picker.Response.DOCUMENTS].length > 0) { 68 const file = data[google.picker.Response.DOCUMENTS][0]; 69 CTEditHelper.GoogleDriveGetFileMetadata(fieldName, file.id, access_token); 70 } else { 71 console.log("No file was selected or the response format has changed."); 72 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = "No file was selected."; 73 } 74 } else if (data[google.picker.Response.ACTION] === google.picker.Action.CANCEL) { 75 console.log("User closed the Picker or canceled selection."); 76 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = "File selection was canceled."; 77 } 78 } 79 80 81 formatFileSize(bytes) { 82 if (bytes === 0) return '0 Bytes'; 83 const k = 1024; 84 const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 85 const i = Math.floor(Math.log(bytes) / Math.log(k)); 86 return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; 87 } 88 89 emptyContainers(boxId, className) { 90 const parentElement = document.getElementById(boxId); 91 92 if (parentElement) { 93 const containers = parentElement.getElementsByClassName(className); 94 95 Array.from(containers).forEach(container => { 96 container.innerHTML = ''; 97 }); 98 } 99 } 100 101 GoogleDriveGetFileMetadata(fieldName, fileId, access_token) { 102 103 gapi.client.load("drive", "v3", () => { 104 gapi.client.drive.files.get({ 105 fileId: fileId, 106 fields: "id, name, mimeType, webContentLink, size" 107 }).then(function (response) { 108 109 CTEditHelper.emptyContainers("ct_uploadfile_box_" + fieldName, "ajax-file-upload-error"); 110 CTEditHelper.emptyContainers("ct_uploadfile_box_" + fieldName, "ajax-file-upload-container"); 111 112 let buttonId = "CustomTablesGoogleDrivePick_" + fieldName; 113 const file = response.result; 114 let prefix; 115 const button = document.getElementById(buttonId); 116 if (button) { 117 const acceptValue = button.dataset.accept; 118 if (acceptValue) { 119 let parts = file.name.toLowerCase().split("."); 120 let fileExtension = parts[parts.length - 1]; 121 let acceptTypes = acceptValue.split(' '); 122 if (acceptTypes.indexOf(fileExtension) === -1) { 123 let content = '<div class="ajax-file-upload-error"><b>' + file.name + '</b> is not allowed. Allowed extensions: ' + acceptValue + '</div>'; 124 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = content; 110 } 111 } 112 113 GoogleDriveGetFileMetadata(fieldName, fileId, access_token) { 114 115 gapi.client.load("drive", "v3", () => { 116 gapi.client.drive.files.get({ 117 fileId: fileId, 118 fields: "id, name, mimeType, webContentLink, size" 119 }).then(function (response) { 120 121 CTEditHelper.emptyContainers("ct_uploadfile_box_" + fieldName, "ajax-file-upload-error"); 122 CTEditHelper.emptyContainers("ct_uploadfile_box_" + fieldName, "ajax-file-upload-container"); 123 124 let buttonId = "CustomTablesGoogleDrivePick_" + fieldName; 125 const file = response.result; 126 let prefix; 127 const button = document.getElementById(buttonId); 128 if (button) { 129 const acceptValue = button.dataset.accept; 130 if (acceptValue) { 131 let parts = file.name.toLowerCase().split("."); 132 let fileExtension = parts[parts.length - 1]; 133 let acceptTypes = acceptValue.split(' '); 134 if (acceptTypes.indexOf(fileExtension) === -1) { 135 let content = '<div class="ajax-file-upload-error"><b>' + file.name + '</b> is not allowed. Allowed extensions: ' + acceptValue + '</div>'; 136 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = content; 137 return; 138 } 139 } else { 140 console.error('Accept file extensions not found.', error); 141 return; 142 } 143 144 prefix = button.dataset.prefix; 145 if (!prefix) { 146 console.error('Prefix not found.', error); 125 147 return; 126 148 } 127 149 } else { 128 console.error(' Accept file extensionsnot found.', error);150 console.error('Button "' + buttonId + '" not found.', error); 129 151 return; 130 152 } 131 153 132 prefix = button.dataset.prefix; 133 if (!prefix) { 134 console.error('Prefix not found.', error); 154 let fileSize = CTEditHelper.formatFileSize(file.size); 155 let content = '<div class="ajax-file-upload-statusbar"><div class="ajax-file-upload-filename">1). ' + file.name + ' (' + fileSize + ')</div></div>'; 156 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = content; 157 document.getElementById(prefix + fieldName + '_filename').value = file.name; 158 159 let data = JSON.stringify({ 160 fileId: file.id, 161 fileName: file.name, 162 //mimeType: file.mimeType, 163 //size: file.size, 164 //downloadUrl: file.webContentLink, 165 accessToken: access_token 166 }) 167 document.getElementById(prefix + fieldName + '_data').value = data; 168 }, function (error) { 169 console.error("Error getting file metadata:", error); 170 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = "Error getting file metadata."; 171 }); 172 }); 173 } 174 175 //A method to create or update table records using JavaScript. CustomTables handles data sanitization and validation. 176 saveRecord(url, fieldsAndValues, listing_id, successCallback, errorCallback) { 177 178 let completeURL = url + '?view=edititem&task=save&tmpl=component&clean=1'; 179 if (listing_id !== undefined && listing_id !== null) 180 completeURL += '&listing_id=' + listing_id; 181 182 let postData = new URLSearchParams(); 183 184 // Iterate over keysObject and append each key-value pair 185 for (const key in fieldsAndValues) { 186 if (fieldsAndValues.hasOwnProperty(key)) { 187 postData.append(ctFieldInputPrefix + key, fieldsAndValues[key]); 188 } 189 } 190 191 fetch(completeURL, { 192 method: 'POST', 193 headers: { 194 'Content-Type': 'application/x-www-form-urlencoded', 195 }, 196 body: postData, 197 }) 198 .then(response => { 199 if (response.redirected) { 200 if (errorCallback && typeof errorCallback === 'function') { 201 errorCallback('Login required or not authorized.'); 202 } else { 203 console.error('Login required or not authorized. Error status code 200: Redirect.'); 204 } 205 return null; 206 } 207 208 if (!response.ok) { 209 // If the HTTP status code is not successful, throw an error object that includes the response 210 throw {status: 'error', message: 'HTTP status code: ' + response.status, response: response}; 211 } 212 return response.json(); 213 }) 214 .then(data => { 215 if (data === null) 135 216 return; 136 } 137 } else { 138 console.error('Button "' + buttonId + '" not found.', error); 139 return; 140 } 141 142 let fileSize = CTEditHelper.formatFileSize(file.size); 143 let content = '<div class="ajax-file-upload-statusbar"><div class="ajax-file-upload-filename">1). ' + file.name + ' (' + fileSize + ')</div></div>'; 144 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = content; 145 document.getElementById(prefix + fieldName + '_filename').value = file.name; 146 147 let data = JSON.stringify({ 148 fileId: file.id, 149 fileName: file.name, 150 //mimeType: file.mimeType, 151 //size: file.size, 152 //downloadUrl: file.webContentLink, 153 accessToken: access_token 217 218 if (data.status === 'saved') { 219 if (successCallback && typeof successCallback === 'function') { 220 successCallback(data); 221 } else { 222 223 } 224 } else if (data.status === 'error') { 225 if (errorCallback && typeof errorCallback === 'function') { 226 errorCallback(data); 227 } else { 228 console.error(data.message); 229 } 230 } 154 231 }) 155 document.getElementById(prefix + fieldName + '_data').value = data; 156 }, function (error) { 157 console.error("Error getting file metadata:", error); 158 document.getElementById("ct_eventsmessage_" + fieldName).innerHTML = "Error getting file metadata."; 232 .catch(error => { 233 if (errorCallback && typeof errorCallback === 'function') { 234 errorCallback({ 235 status: 'error', 236 message: 'An error occurred during the request.', 237 }); 238 } else { 239 console.error('Error', error); 240 console.log(completeURL); 241 } 242 }); 243 } 244 245 //TODO: no usages found 246 async refreshRecord(url, listing_id, successCallback, errorCallback, ModuleId) { 247 let completeURL = url + '?tmpl=component&clean=1&task=refresh'; 248 if (listing_id !== undefined && listing_id !== null) 249 completeURL += '&ids=' + listing_id; 250 251 try { 252 const response = await fetch(completeURL); 253 if (!response.ok) { 254 throw new Error('Network response was not ok'); 255 } 256 const data = await response.json(); 257 console.log(data); 258 } catch (error) { 259 console.error('There was a problem with the fetch operation:', error); 260 } 261 262 //let postData = new URLSearchParams(); 263 //postData.append('task', 'refresh'); 264 265 fetch(completeURL, { 266 method: 'GET' 267 }) 268 .then(response => { 269 270 if (response.redirected) { 271 if (errorCallback && typeof errorCallback === 'function') { 272 errorCallback('Login required or not authorized.'); 273 } else { 274 console.error('Login required or not authorized. Error status code 200: Redirect.'); 275 } 276 return null; 277 } 278 279 if (!response.ok) { 280 // If the HTTP status code is not successful, throw an error object that includes the response 281 throw {status: 'error', message: 'HTTP status code: ' + response.status, response: response}; 282 } 283 return response.json(); 284 }) 285 .then(data => { 286 if (data === null) 287 return; 288 289 if (data.status === 'saved') { 290 if (successCallback && typeof successCallback === 'function') { 291 successCallback(data); 292 } else { 293 294 } 295 } else if (data.status === 'error') { 296 if (errorCallback && typeof errorCallback === 'function') { 297 errorCallback(data); 298 } else { 299 console.error(data.message); 300 } 301 } 302 }) 303 .catch(error => { 304 if (errorCallback && typeof errorCallback === 'function') { 305 errorCallback({ 306 status: 'error', 307 message: 'An error occurred during the request.', 308 }); 309 } else { 310 console.error('Error 145:', error); 311 console.log(completeURL); 312 } 313 }); 314 } 315 316 //Reloads a particular table row (record) after changes have been made. It identifies the table and the specific row based on the provided listing_id and then triggers a refresh to update the displayed data. 317 reloadRecord(listing_id) { 318 319 // Select all table elements whose id attribute starts with 'ctTable_' 320 const tables = document.querySelectorAll('table[id^="ctTable_"]'); 321 tables.forEach(table => { 322 let parts = table.id.split("_"); 323 if (parts.length === 2) { 324 let tableId = parts[1]; 325 let trId = 'ctTable_' + tableId + '_' + listing_id; 326 const records = table.querySelectorAll('tr[id^="' + trId + '"]'); 327 if (records.length == 1) { 328 let table_object = findTableByRowId(tableid + '_' + listing_id); 329 let index = findRowIndexById(table_object, tableId, listing_id, 'ctEditIcon'); 330 ctCatalogUpdate(tableId, listing_id, index, ModuleId); 331 } 332 } 159 333 }); 160 }); 161 } 162 163 //A method to create or update table records using JavaScript. CustomTables handles data sanitization and validation. 164 saveRecord(url, fieldsAndValues, listing_id, successCallback, errorCallback) { 165 166 let completeURL = url + '?view=edititem&task=save&tmpl=component&clean=1'; 167 if (listing_id !== undefined && listing_id !== null) 168 completeURL += '&listing_id=' + listing_id; 169 170 let postData = new URLSearchParams(); 171 172 // Iterate over keysObject and append each key-value pair 173 for (const key in fieldsAndValues) { 174 if (fieldsAndValues.hasOwnProperty(key)) { 175 postData.append(ctFieldInputPrefix + key, fieldsAndValues[key]); 176 } 177 } 178 179 fetch(completeURL, { 180 method: 'POST', 181 headers: { 182 'Content-Type': 'application/x-www-form-urlencoded', 183 }, 184 body: postData, 185 }) 186 .then(response => { 187 if (response.redirected) { 188 if (errorCallback && typeof errorCallback === 'function') { 189 errorCallback('Login required or not authorized.'); 190 } else { 191 console.error('Login required or not authorized. Error status code 200: Redirect.'); 192 } 193 return null; 194 } 195 196 if (!response.ok) { 197 // If the HTTP status code is not successful, throw an error object that includes the response 198 throw {status: 'error', message: 'HTTP status code: ' + response.status, response: response}; 199 } 200 return response.json(); 201 }) 202 .then(data => { 203 if (data === null) 204 return; 205 206 if (data.status === 'saved') { 207 if (successCallback && typeof successCallback === 'function') { 208 successCallback(data); 209 } else { 210 211 } 212 } else if (data.status === 'error') { 213 if (errorCallback && typeof errorCallback === 'function') { 214 errorCallback(data); 215 } else { 216 console.error(data.message); 217 } 218 } 219 }) 220 .catch(error => { 221 if (errorCallback && typeof errorCallback === 'function') { 222 errorCallback({ 223 status: 'error', 224 message: 'An error occurred during the request.', 225 }); 226 } else { 227 console.error('Error', error); 228 console.log(completeURL); 229 } 230 }); 231 } 232 233 //TODO: no usages found 234 async refreshRecord(url, listing_id, successCallback, errorCallback, ModuleId) { 235 let completeURL = url + '?tmpl=component&clean=1&task=refresh'; 236 if (listing_id !== undefined && listing_id !== null) 237 completeURL += '&ids=' + listing_id; 238 239 try { 240 const response = await fetch(completeURL); 241 if (!response.ok) { 242 throw new Error('Network response was not ok'); 243 } 244 const data = await response.json(); 245 console.log(data); 246 } catch (error) { 247 console.error('There was a problem with the fetch operation:', error); 248 } 249 250 //let postData = new URLSearchParams(); 251 //postData.append('task', 'refresh'); 252 253 fetch(completeURL, { 254 method: 'GET' 255 }) 256 .then(response => { 257 258 if (response.redirected) { 259 if (errorCallback && typeof errorCallback === 'function') { 260 errorCallback('Login required or not authorized.'); 261 } else { 262 console.error('Login required or not authorized. Error status code 200: Redirect.'); 263 } 264 return null; 265 } 266 267 if (!response.ok) { 268 // If the HTTP status code is not successful, throw an error object that includes the response 269 throw {status: 'error', message: 'HTTP status code: ' + response.status, response: response}; 270 } 271 return response.json(); 272 }) 273 .then(data => { 274 if (data === null) 275 return; 276 277 if (data.status === 'saved') { 278 if (successCallback && typeof successCallback === 'function') { 279 successCallback(data); 280 } else { 281 282 } 283 } else if (data.status === 'error') { 284 if (errorCallback && typeof errorCallback === 'function') { 285 errorCallback(data); 286 } else { 287 console.error(data.message); 288 } 289 } 290 }) 291 .catch(error => { 292 if (errorCallback && typeof errorCallback === 'function') { 293 errorCallback({ 294 status: 'error', 295 message: 'An error occurred during the request.', 296 }); 297 } else { 298 console.error('Error 145:', error); 299 console.log(completeURL); 300 } 301 }); 302 } 303 304 //Reloads a particular table row (record) after changes have been made. It identifies the table and the specific row based on the provided listing_id and then triggers a refresh to update the displayed data. 305 reloadRecord(listing_id) { 306 307 // Select all table elements whose id attribute starts with 'ctTable_' 308 const tables = document.querySelectorAll('table[id^="ctTable_"]'); 309 tables.forEach(table => { 310 let parts = table.id.split("_"); 311 if (parts.length === 2) { 312 let tableId = parts[1]; 313 let trId = 'ctTable_' + tableId + '_' + listing_id; 314 const records = table.querySelectorAll('tr[id^="' + trId + '"]'); 315 if (records.length == 1) { 316 let index = findRowIndexById(table.id, trId); 317 ctCatalogUpdate(tableId, listing_id, index, ModuleId); 318 } 319 } 320 }); 321 } 322 323 ImageGalleryInitImagePreviews(inputId) { 324 const input = document.getElementById(inputId); 325 326 input.onchange = function (event) { 327 const previewContainer = document.getElementById(inputId + '_previewNew'); 328 previewContainer.innerHTML = ''; 329 330 Array.from(event.target.files).forEach((file, index) => { 331 if (file.type.startsWith('image/')) { 332 const reader = new FileReader(); 333 const div = document.createElement('div'); 334 div.className = 'preview-item'; 335 div.dataset.fileIndex = index; 336 337 reader.onload = function (e) { 338 div.innerHTML = ` 334 } 335 336 ImageGalleryInitImagePreviews(inputId) { 337 const input = document.getElementById(inputId); 338 339 input.onchange = function (event) { 340 const previewContainer = document.getElementById(inputId + '_previewNew'); 341 previewContainer.innerHTML = ''; 342 343 Array.from(event.target.files).forEach((file, index) => { 344 if (file.type.startsWith('image/')) { 345 const reader = new FileReader(); 346 const div = document.createElement('div'); 347 div.className = 'preview-item'; 348 div.dataset.fileIndex = index; 349 350 reader.onload = function (e) { 351 div.innerHTML = ` 339 352 <img src="${e.target.result}" class="preview-image" /> 340 353 <button type="button" class="remove-btn" 341 354 onclick="CTEditHelper.ImageGalleryRemoveFile(this, '${inputId}', ${index})">×</button> 342 355 `; 343 }; 344 345 previewContainer.appendChild(div); 346 reader.readAsDataURL(file); 347 } 348 }); 349 }; 350 } 351 352 ImageGalleryRemoveFile(button, inputId, fileIndex) { 353 354 if (fileIndex < 0) { 355 //mark to delete existing file 356 const input = document.getElementById(inputId + '_uploaded'); 357 if (input) { 358 let files = input.value.split(','); 359 let newFiles = []; 356 }; 357 358 previewContainer.appendChild(div); 359 reader.readAsDataURL(file); 360 } 361 }); 362 }; 363 } 364 365 ImageGalleryRemoveFile(button, inputId, fileIndex) { 366 367 if (fileIndex < 0) { 368 //mark to delete existing file 369 const input = document.getElementById(inputId + '_uploaded'); 370 if (input) { 371 let files = input.value.split(','); 372 let newFiles = []; 373 374 for (let i = 0; i < files.length; i++) { 375 if (parseInt(files[i]) != -fileIndex) 376 newFiles.push(files[i]); 377 else 378 newFiles.push(fileIndex); 379 } 380 381 input.value = newFiles.join(','); 382 const container = button.closest('.preview-item'); 383 container.remove(); 384 } 385 } else { 386 const input = document.getElementById(inputId); 387 const container = button.closest('.preview-item'); 388 389 const dt = new DataTransfer(); 390 const files = input.files; 360 391 361 392 for (let i = 0; i < files.length; i++) { 362 if (parseInt(files[i]) != -fileIndex) 363 newFiles.push(files[i]); 364 else 365 newFiles.push(fileIndex); 366 } 367 368 input.value = newFiles.join(','); 369 const container = button.closest('.preview-item'); 393 if (i !== fileIndex) { 394 dt.items.add(files[i]); 395 } 396 } 397 398 input.files = dt.files; 370 399 container.remove(); 371 } 372 } else { 373 const input = document.getElementById(inputId); 374 const container = button.closest('.preview-item'); 375 376 const dt = new DataTransfer(); 377 const files = input.files; 378 379 for (let i = 0; i < files.length; i++) { 380 if (i !== fileIndex) { 381 dt.items.add(files[i]); 382 } 383 } 384 385 input.files = dt.files; 386 container.remove(); 387 388 // Reindex remaining previews 389 const previews = document.querySelectorAll('.preview-item'); 390 previews.forEach((preview, index) => { 391 preview.dataset.fileIndex = index; 392 const removeBtn = preview.querySelector('.remove-btn'); 393 removeBtn.setAttribute('onclick', `CTEditHelper.ImageGalleryRemoveFile(this, '${inputId}', ${index})`); 394 }); 395 } 396 } 397 398 checkRequiredFields(formObject) { 399 if (!checkFilters()) 400 return false; 401 402 if (ct_signaturePad_fields.length > 0) { 403 if (!ctInputbox_signature_apply()) { 404 event.preventDefault(); 400 401 // Reindex remaining previews 402 const previews = document.querySelectorAll('.preview-item'); 403 previews.forEach((preview, index) => { 404 preview.dataset.fileIndex = index; 405 const removeBtn = preview.querySelector('.remove-btn'); 406 removeBtn.setAttribute('onclick', `CTEditHelper.ImageGalleryRemoveFile(this, '${inputId}', ${index})`); 407 }); 408 } 409 } 410 411 checkRequiredFields(formObject) { 412 if (!checkFilters()) 405 413 return false; 406 } 407 } 408 409 let requiredFields = formObject.getElementsByClassName("required"); 410 let label = "One field"; 411 412 for (let i = 0; i < requiredFields.length; i++) { 413 if (typeof requiredFields[i].id != "undefined") { 414 if (requiredFields[i].id.indexOf("sqljoin_table_" + ctFieldInputPrefix) !== -1) { 415 if (!CheckSQLJoinRadioSelections(requiredFields[i].id)) 416 return false; 417 } 418 if (requiredFields[i].id.indexOf("ct_uploadfile_box_") !== -1) { 419 if (!CheckImageUploader(requiredFields[i].id)) { 414 415 if (this.ct_signaturePad_fields.length > 0) { 416 if (!CTEditHelper.ctInputbox_signature_apply()) { 417 event.preventDefault(); 418 return false; 419 } 420 } 421 422 let requiredFields = formObject.getElementsByClassName("required"); 423 let label = "One field"; 424 425 for (let i = 0; i < requiredFields.length; i++) { 426 if (typeof requiredFields[i].id != "undefined") { 427 if (requiredFields[i].id.indexOf("sqljoin_table_" + ctFieldInputPrefix) !== -1) { 428 if (!CheckSQLJoinRadioSelections(requiredFields[i].id)) 429 return false; 430 } 431 if (requiredFields[i].id.indexOf("ct_uploadfile_box_") !== -1) { 432 if (!CheckImageUploader(requiredFields[i].id)) { 433 let d = requiredFields[i].dataset; 434 if (d.label) 435 label = d.label; 436 else 437 label = "Unlabeled field"; 438 439 let imageObjectName = requiredFields[i].id + '_image'; 440 let imageObject = document.getElementById(imageObjectName); 441 442 if (imageObject) 443 return true; 444 445 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 446 return false; 447 } 448 } 449 } 450 451 if (typeof requiredFields[i].name != "undefined") { 452 let n = requiredFields[i].name.toString(); 453 454 if (n.indexOf(ctFieldInputPrefix) !== -1) { 455 456 let objName = n.replace('_selector', ''); 457 420 458 let d = requiredFields[i].dataset; 421 459 if (d.label) 422 label = d.label ;460 label = d.label 423 461 else 424 462 label = "Unlabeled field"; 425 463 426 let imageObjectName = requiredFields[i].id + '_image'; 427 let imageObject = document.getElementById(imageObjectName); 428 429 if (imageObject) 430 return true; 431 432 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 433 return false; 434 } 435 } 436 } 437 438 if (typeof requiredFields[i].name != "undefined") { 439 let n = requiredFields[i].name.toString(); 440 441 if (n.indexOf(ctFieldInputPrefix) !== -1) { 442 443 let objName = n.replace('_selector', ''); 444 445 let d = requiredFields[i].dataset; 446 if (d.label) 447 label = d.label 448 else 449 label = "Unlabeled field"; 450 451 if (d.type === 'sqljoin') { 452 if (requiredFields[i].type === "hidden") { 464 if (d.type === 'sqljoin') { 465 if (requiredFields[i].type === "hidden") { 466 let obj = document.getElementById(objName); 467 468 if (obj.value === '') { 469 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 470 return false; 471 } 472 } 473 474 } else if (requiredFields[i].type === "text") { 453 475 let obj = document.getElementById(objName); 454 455 476 if (obj.value === '') { 456 477 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 457 478 return false; 458 479 } 480 } else if (requiredFields[i].type === "select-one") { 481 let obj = document.getElementById(objName); 482 483 if (obj.value === null || obj.value === '') { 484 alert(TranslateText('COM_CUSTOMTABLES_NOT_SELECTED', label)); 485 return false; 486 } 487 } else if (requiredFields[i].type === "select-multiple") { 488 let count_multiple_obj = document.getElementById(lbln); 489 let options = count_multiple_obj.options; 490 let count_multiple = 0; 491 492 for (let i2 = 0; i2 < options.length; i2++) { 493 if (options[i2].selected) 494 count_multiple++; 495 } 496 497 if (count_multiple === 0) { 498 alert(TranslateText('COM_CUSTOMTABLES_NOT_SELECTED', label)); 499 return false; 500 } 501 } else if (d.selector == 'switcher') { 502 //Checkbox element with Yes/No visual effect 503 if (d.label) 504 label = d.label; 505 else 506 label = "Unlabeled field"; 507 508 if (requiredFields[i].value === "1") { 509 510 if (d.valuerulecaption && d.valuerulecaption !== "") 511 alert(d.valuerulecaption); 512 else 513 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 514 return false; 515 } 516 } else if (d.type == 'checkbox') { 517 //Simple HTML Checkbox element 518 if (d.label) 519 label = d.label; 520 else 521 label = "Unlabeled field"; 522 523 if (!requiredFields[i].checked) { 524 if (d.valuerulecaption && d.valuerulecaption !== "") 525 alert(d.valuerulecaption); 526 else 527 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 528 return false; 529 } 459 530 } 460 461 } else if (requiredFields[i].type === "text") { 462 let obj = document.getElementById(objName); 463 if (obj.value === '') { 464 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 465 return false; 531 } 532 } 533 } 534 return true; 535 } 536 537 convertDateTypeValues(elements) { 538 for (let i = 0; i < elements.length; i++) { 539 if (elements[i].name && elements[i].name !== '' && elements[i].name !== 'returnto') { 540 if (elements[i].dataset.type === "date") { 541 if (elements[i].dataset.format !== "%Y-%m-%d") { 542 //convert date to %Y-%m-%d 543 let dateValue = elements[i].value; 544 if (dateValue) { 545 // Parse the format string 546 let format = elements[i].dataset.format; 547 let day, month, year; 548 549 // Convert Joomla's format to parts 550 let parts = dateValue.split(/[-/.]/); 551 let formatParts = format.split(/[-/.]/); 552 553 // Map the parts to corresponding values 554 formatParts.forEach((part, index) => { 555 if (part === '%d') day = parts[index]; 556 else if (part === '%m') month = parts[index]; 557 else if (part === '%Y') year = parts[index]; 558 }); 559 560 // Create standardized date string 561 elements[i].value = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`; 562 } 466 563 } 467 } else if (requiredFields[i].type === "select-one") { 468 let obj = document.getElementById(objName); 469 470 if (obj.value === null || obj.value === '') { 471 alert(TranslateText('COM_CUSTOMTABLES_NOT_SELECTED', label)); 472 return false; 564 } 565 } 566 } 567 } 568 569 ctInputbox_signature(inputbox_id, width, height, format) { 570 571 let canvas = document.getElementById(inputbox_id + '_canvas'); 572 573 this.ct_signaturePad_fields.push(inputbox_id); 574 this.ct_signaturePad[inputbox_id] = new SignaturePad(canvas, { 575 backgroundColor: "rgb(255, 255, 255)" 576 }); 577 578 this.ct_signaturePad_formats[inputbox_id] = format; 579 580 canvas.width = width; 581 canvas.height = height; 582 canvas.getContext("2d").scale(1, 1); 583 584 document.getElementById(inputbox_id + '_clear').addEventListener('click', function () { 585 this.ct_signaturePad[inputbox_id].clear(); 586 }); 587 } 588 589 ctInputbox_signature_apply() { 590 591 if (this.ct_signaturePad_fields.length === 0) 592 return true; 593 594 let inputbox_id = this.ct_signaturePad_fields[0]; 595 596 if (this.ct_signaturePad[inputbox_id].isEmpty()) { 597 alert(TranslateText('COM_CUSTOMTABLES_JS_SIGNATURE_REQUIRED')); 598 return false; 599 } else { 600 601 let format = this.ct_signaturePad_formats[inputbox_id]; 602 603 let dataURL = this.ct_signaturePad[inputbox_id].toDataURL('image/' + format); 604 document.getElementById(inputbox_id).setAttribute("value", dataURL); 605 return true; 606 } 607 } 608 609 ctInputbox_UpdateSQLJoinLink(control_name, control_name_postfix) { 610 //Old calls replaced 611 setTimeout(this.ctInputbox_UpdateSQLJoinLink_do(control_name, control_name_postfix), 100); 612 } 613 614 ctInputbox_UpdateSQLJoinLink_do(control_name, control_name_postfix) { 615 //Old calls replaced 616 let controlElement = document.getElementById(control_name); 617 let selectedControlElements = Array.from(controlElement.options) 618 .filter(option => option.selected) 619 .map(option => option.value); 620 621 let l = document.getElementById(control_name + control_name_postfix); 622 let o = document.getElementById(control_name + 'SQLJoinLink'); 623 let v = ''; 624 625 if (o) { 626 if (o.selectedIndex === -1) 627 return false; 628 629 v = o.options[o.selectedIndex].value; 630 } 631 632 let selectedValue = null 633 let ctInputBoxRecords_current_value = document.getElementById(control_name + '_ctInputBoxRecords_current_value'); 634 635 if (ctInputBoxRecords_current_value) 636 selectedValue = String(ctInputBoxRecords_current_value.innerHTML); 637 638 ctInputBoxRecords_removeOptions(l); 639 640 if (control_name_postfix !== '_selector') { 641 let opt = document.createElement("option"); 642 opt.value = '0'; 643 opt.innerHTML = TranslateText('COM_CUSTOMTABLES_SELECT'); 644 l.appendChild(opt); 645 } 646 647 let elements = JSON.parse(document.getElementById(control_name + control_name_postfix + '_elements').textContent); 648 let elementsID = document.getElementById(control_name + control_name_postfix + '_elementsID').innerHTML.split(","); 649 let elementsPublished = document.getElementById(control_name + control_name_postfix + '_elementsPublished').innerHTML.split(","); 650 651 let filterElement = document.getElementById(control_name + control_name_postfix + '_elementsFilter'); 652 let elementsFilter = [] 653 if (filterElement) 654 elementsFilter = filterElement.innerHTML.split(";"); 655 656 for (let i = 0; i < elements.length; i++) { 657 let f = elementsFilter[i]; 658 659 if (elements[i] !== "") { 660 661 let eid = String(elementsID[i]); 662 if (selectedControlElements.indexOf(eid) === -1) { 663 664 let published = parseInt(elementsPublished[i]); 665 666 if (typeof f != "undefined") { 667 let f_list = f.split(","); 668 669 if (f_list.indexOf(v) !== -1) { 670 let opt = document.createElement("option"); 671 opt.value = eid; 672 if (eid === selectedValue) 673 opt.selected = true; 674 675 if (published === 0) 676 opt.style.cssText = "color:red;"; 677 678 opt.innerHTML = elements[i]; 679 l.appendChild(opt); 680 } 681 } else { 682 683 let opt = document.createElement("option"); 684 opt.value = eid; 685 if (eid === selectedValue) 686 opt.selected = true; 687 688 if (published === 0) 689 opt.style.cssText = "color:red;"; 690 691 opt.innerHTML = elements[i]; 692 l.appendChild(opt); 473 693 } 474 } else if (requiredFields[i].type === "select-multiple") { 475 let count_multiple_obj = document.getElementById(lbln); 476 let options = count_multiple_obj.options; 477 let count_multiple = 0; 478 479 for (let i2 = 0; i2 < options.length; i2++) { 480 if (options[i2].selected) 481 count_multiple++; 482 } 483 484 if (count_multiple === 0) { 485 alert(TranslateText('COM_CUSTOMTABLES_NOT_SELECTED', label)); 486 return false; 487 } 488 } else if (d.selector == 'switcher') { 489 //Checkbox element with Yes/No visual effect 490 if (d.label) 491 label = d.label; 492 else 493 label = "Unlabeled field"; 494 495 if (requiredFields[i].value === "1") { 496 497 if (d.valuerulecaption && d.valuerulecaption !== "") 498 alert(d.valuerulecaption); 499 else 500 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 501 return false; 502 } 503 } else if (d.type == 'checkbox') { 504 //Simple HTML Checkbox element 505 if (d.label) 506 label = d.label; 507 else 508 label = "Unlabeled field"; 509 510 if (!requiredFields[i].checked) { 511 if (d.valuerulecaption && d.valuerulecaption !== "") 512 alert(d.valuerulecaption); 513 else 514 alert(TranslateText('COM_CUSTOMTABLES_REQUIRED', label)); 515 return false; 516 } 517 } 518 } 519 } 520 } 521 return true; 522 } 523 524 convertDateTypeValues(elements) { 525 for (let i = 0; i < elements.length; i++) { 526 if (elements[i].name && elements[i].name !== '' && elements[i].name !== 'returnto') { 527 if (elements[i].dataset.type === "date") { 528 if (elements[i].dataset.format !== "%Y-%m-%d") { 529 //convert date to %Y-%m-%d 530 let dateValue = elements[i].value; 531 if (dateValue) { 532 // Parse the format string 533 let format = elements[i].dataset.format; 534 let day, month, year; 535 536 // Convert Joomla's format to parts 537 let parts = dateValue.split(/[-/.]/); 538 let formatParts = format.split(/[-/.]/); 539 540 // Map the parts to corresponding values 541 formatParts.forEach((part, index) => { 542 if (part === '%d') day = parts[index]; 543 else if (part === '%m') month = parts[index]; 544 else if (part === '%Y') year = parts[index]; 545 }); 546 547 // Create standardized date string 548 elements[i].value = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`; 549 } 550 } 551 } 552 } 553 } 554 } 694 } 695 } 696 } 697 698 return true; 699 } 700 701 ctInputBoxRecords_addItem(control_name, control_name_postfix) { 702 703 let o = document.getElementById(control_name + control_name_postfix); 704 o.selectedIndex = 0; 705 706 if (this.ctInputBoxRecords_dynamic_filter[control_name] !== '') { 707 708 let ctInputBoxRecords_current_value = document.getElementById(control_name + '_ctInputBoxRecords_current_value'); 709 if (ctInputBoxRecords_current_value) 710 ctInputBoxRecords_current_value.innerHTML = ''; 711 712 let SQLJoinLink = document.getElementById(control_name + control_name_postfix + 'SQLJoinLink'); 713 if (SQLJoinLink)// { 714 SQLJoinLink.selectedIndex = 0; 715 716 this.ctInputbox_UpdateSQLJoinLink(control_name, control_name_postfix); 717 } 718 719 document.getElementById(control_name + '_addButton').style.visibility = "hidden"; 720 document.getElementById(control_name + '_addBox').style.visibility = "visible"; 721 } 722 } 723 724 globalThis.CustomTablesEdit = CustomTablesEdit; // Store globally 555 725 } 556 726 … … 616 786 617 787 let fieldsProcessed = []; 618 let params = ""; 788 let params = new URLSearchParams(); 789 619 790 let opt; 620 791 for (let i = 0; i < elements.length; i++) { … … 628 799 opt = options[x]; 629 800 if (opt.selected) 630 params += "&" + elements[i].name + "=" + opt.value;801 params.append(elements[i].name, opt.value); 631 802 } 632 803 633 804 } else if (elements[i].type === "checkbox") { 634 805 // Handle checkboxes: add "true" if checked, "false" if unchecked 635 params += "&" + elements[i].name + "=" + (elements[i].checked ? "true" : "false");806 params.append(elements[i].name, (elements[i].checked ? "true" : "false")); 636 807 } else if (elements[i].type === "radio") { 637 808 // Handle radio buttons: Check if any radio button with the same name is selected … … 641 812 for (let r = 0; r < radios.length; r++) { 642 813 if (radios[r].checked) { 643 params += "&" + radios[r].name + "=" + radios[r].value;814 params.append(radios[r].name, radios[r].value); 644 815 radioChecked = true; 645 816 break; // No need to check further once one is selected … … 649 820 // If no radio button is selected, set a default value (if desired) 650 821 if (!radioChecked) { 651 params += "&" + elements[i].name + "=none"; // You can set "none" or another default value822 params.append(elements[i].name, "none"); 652 823 } 653 824 } else { 654 params += "&" + elements[i].name + "=" + elements[i].value;825 params.append(elements[i].name, elements[i].value); 655 826 } 656 827 fieldsProcessed.push(elements[i].name); … … 661 832 662 833 if (http) { 663 664 http.open("POST", url + "&clean=1&ctmodalform=1&load=1", true); 834 params.append('task', "save"); 835 params.append('clean', "1"); 836 params.append('frmt', "json"); 837 params.append('ctmodalform', "1"); 838 params.append('load', "1"); 839 840 let clean_url = url.replace('%addRecord%', ''); 841 console.log("clean_url:", clean_url) 842 console.log("params:", params.toString()) 843 844 http.open("POST", clean_url, true); 665 845 http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 846 http.setRequestHeader("X-Requested-With", "XMLHttpRequest"); // Prevent full-page redirects 847 666 848 http.onreadystatechange = function () { 667 849 if (http.readyState === 4) { … … 669 851 670 852 try { 853 let responseString = http.response.toString(); 854 console.log('responseString:', responseString); 671 855 response = JSON.parse(http.response.toString()); 672 856 } catch (e) { 673 console.log(url + "&clean=1&ctmodalform=1&load=1"); 674 console.log(http.response.toString()); 675 return console.error(e); 676 } 677 678 if (response.status === "saved") { 679 let element_tableid_tr = "ctTable_" + tableid + '_' + recordId; 857 858 let r = http.response.toString(); 859 if (r.indexOf('view-login') !== -1) { 860 alert('Session expired. Please login again.'); 861 location.reload(); 862 return; 863 } else { 864 console.log(clean_url); 865 console.log(http.response.toString()); 866 return console.error(e); 867 } 868 } 869 870 if (response.success) { 871 //let element_tableid_tr = "ctTable_" + tableid + '_' + recordId; 680 872 let table_object = document.getElementById("ctTable_" + tableid); 681 873 682 874 if (table_object) { 683 let index = findRowIndexById( "ctTable_" + tableid, element_tableid_tr);875 let index = findRowIndexById(table_object, tableid, recordId, 'ctEditIcon'); 684 876 ctCatalogUpdate(tableid, recordId, index, ModuleId); 685 877 } … … 691 883 } 692 884 693 if (hideModelOnSave) 885 if (hideModelOnSave) { 694 886 ctHidePopUp(); 887 return; 888 } 695 889 696 890 if (returnLinkEncoded !== "") … … 698 892 699 893 } else { 894 /* 700 895 if (http.response.indexOf('<div class="alert-message">Nothing to save</div>') !== -1) 701 896 alert('Nothing to save. Check Edit From layout.'); … … 703 898 alert(TranslateText('COM_CUSTOMTABLES_JS_SESSION_EXPIRED')); 704 899 else 705 alert(http.response); 900 */ 901 alert(response.message); 706 902 } 707 903 } 708 904 }; 709 http.send(params );905 http.send(params.toString()); 710 906 } 711 907 } … … 1071 1267 let js = 'ctTableJoinAddRecordModalForm(\'' + control_name + '\',' + sub_index + ');'; 1072 1268 let addText = TranslateText('COM_CUSTOMTABLES_ADD'); 1073 NoItemsText = addText + '<a href="javascript:' + js + '" className="toolbarIcons"><img src="' + ctWebsiteRoot + 'components/com_customtables/libraries/customtables/media/images/icons/new.png" alt="' + addText + '" title="' + addText + '"></a>';1269 NoItemsText = addText + '<a href="javascript:' + js + '" className="toolbarIcons"><img src="' + CTEditHelper.websiteRoot + 'components/com_customtables/libraries/customtables/media/images/icons/new.png" alt="' + addText + '" title="' + addText + '"></a>'; 1074 1270 } else 1075 1271 NoItemsText = TranslateText('COM_CUSTOMTABLES_SELECT_NOTHING') … … 1133 1329 let wrapper = document.getElementById(control_name + "Wrapper"); 1134 1330 1135 let query = ctWebsiteRoot + 'index.php/'+ wrapper.dataset.addrecordmenualias;1331 let query = CTEditHelper.websiteRoot + 'index.php' + (wrapper.dataset.addrecordmenualias.indexOf('/') === -1 ? '/' : '') + wrapper.dataset.addrecordmenualias; 1136 1332 if (wrapper.dataset.addrecordmenualias.indexOf('?') === -1) 1137 1333 query += '?'; … … 1159 1355 1160 1356 if (link.length === 2)//to make sure that it will work in the back-end 1161 url = ctWebsiteRoot + 'administrator/index.php?option=com_customtables&view=records&frmt=json&key=' + wrapper.dataset.key + '&index=' + index;1357 url = CTEditHelper.websiteRoot + 'administrator/index.php?option=com_customtables&view=records&frmt=json&key=' + wrapper.dataset.key + '&index=' + index; 1162 1358 else 1163 url = ctWebsiteRoot + 'index.php?option=com_customtables&view=catalog&tmpl=component&frmt=json&key=' + wrapper.dataset.key + '&index=' + index;1359 url = CTEditHelper.websiteRoot + 'index.php?option=com_customtables&view=catalog&tmpl=component&frmt=json&key=' + wrapper.dataset.key + '&index=' + index; 1164 1360 1165 1361 } else if (CTEditHelper.cmsName === "WordPress") { 1166 url = ctWebsiteRoot + 'index.php?page=customtables-api-tablejoin&key=' + wrapper.dataset.key + '&index=' + index;1362 url = CTEditHelper.websiteRoot + 'index.php?page=customtables-api-tablejoin&key=' + wrapper.dataset.key + '&index=' + index; 1167 1363 } 1168 1364 … … 1249 1445 response = JSON.parse(http.response.toString()); 1250 1446 } catch (e) { 1251 console.log(http.response.toString());1252 console.log(url);1253 1447 return console.error(e); 1254 1448 } … … 1261 1455 1262 1456 // --------------------- Inputbox: Records 1263 let ctInputBoxRecords_dynamic_filter = []; 1457 1264 1458 1265 1459 function ctInputBoxRecords_removeOptions(selectobj) { … … 1270 1464 } 1271 1465 1272 function ctInputBoxRecords_addItem(control_name, control_name_postfix) {1273 1274 let o = document.getElementById(control_name + control_name_postfix);1275 o.selectedIndex = 0;1276 1277 if (ctInputBoxRecords_dynamic_filter[control_name] !== '') {1278 1279 let ctInputBoxRecords_current_value = document.getElementById(control_name + '_ctInputBoxRecords_current_value');1280 if (ctInputBoxRecords_current_value)1281 ctInputBoxRecords_current_value.innerHTML = '';1282 1283 let SQLJoinLink = document.getElementById(control_name + control_name_postfix + 'SQLJoinLink');1284 if (SQLJoinLink)// {1285 SQLJoinLink.selectedIndex = 0;1286 ctInputbox_UpdateSQLJoinLink(control_name, control_name_postfix);1287 }1288 1289 document.getElementById(control_name + '_addButton').style.visibility = "hidden";1290 document.getElementById(control_name + '_addBox').style.visibility = "visible";1291 }1292 1466 1293 1467 function ctInputBoxRecords_DoAddItem(control_name, control_name_postfix) { … … 1345 1519 if (isHidden) { 1346 1520 ctInputBoxRecords_cancel(control_name, '_selector'); 1347 ctInputBoxRecords_addItem(control_name, '_selector')1521 CTEditHelper.ctInputBoxRecords_addItem(control_name, '_selector') 1348 1522 } 1349 1523 } … … 1374 1548 1375 1549 if (CTEditHelper.cmsName === "Joomla") 1376 deleteImage = ctWebsiteRoot + 'components/com_customtables/libraries/customtables/media/images/icons/cancel.png';1550 deleteImage = CTEditHelper.websiteRoot + 'components/com_customtables/libraries/customtables/media/images/icons/cancel.png'; 1377 1551 else if (CTEditHelper.cmsName === "WordPress") 1378 deleteImage = ctWebsiteRoot + 'wp-content/plugins/customtables/libraries/customtables/media/images/icons/cancel.png';1552 deleteImage = CTEditHelper.websiteRoot + 'wp-content/plugins/customtables/libraries/customtables/media/images/icons/cancel.png'; 1379 1553 1380 1554 v += '<td style="border-bottom:1px dotted grey;min-width:16px;">'; … … 1421 1595 } 1422 1596 1423 function ctInputbox_UpdateSQLJoinLink(control_name, control_name_postfix) {1424 //Old calls replaced1425 setTimeout(ctInputbox_UpdateSQLJoinLink_do(control_name, control_name_postfix), 100);1426 }1427 1428 function ctInputbox_UpdateSQLJoinLink_do(control_name, control_name_postfix) {1429 //Old calls replaced1430 let controlElement = document.getElementById(control_name);1431 let selectedControlElements = Array.from(controlElement.options)1432 .filter(option => option.selected)1433 .map(option => option.value);1434 1435 let l = document.getElementById(control_name + control_name_postfix);1436 let o = document.getElementById(control_name + 'SQLJoinLink');1437 let v = '';1438 1439 if (o) {1440 if (o.selectedIndex === -1)1441 return false;1442 1443 v = o.options[o.selectedIndex].value;1444 }1445 1446 let selectedValue = null1447 let ctInputBoxRecords_current_value = document.getElementById(control_name + '_ctInputBoxRecords_current_value');1448 1449 if (ctInputBoxRecords_current_value)1450 selectedValue = String(ctInputBoxRecords_current_value.innerHTML);1451 1452 ctInputBoxRecords_removeOptions(l);1453 1454 if (control_name_postfix !== '_selector') {1455 let opt = document.createElement("option");1456 opt.value = '0';1457 opt.innerHTML = TranslateText('COM_CUSTOMTABLES_SELECT');1458 l.appendChild(opt);1459 }1460 1461 let elements = JSON.parse(document.getElementById(control_name + control_name_postfix + '_elements').textContent);1462 let elementsID = document.getElementById(control_name + control_name_postfix + '_elementsID').innerHTML.split(",");1463 let elementsPublished = document.getElementById(control_name + control_name_postfix + '_elementsPublished').innerHTML.split(",");1464 1465 let filterElement = document.getElementById(control_name + control_name_postfix + '_elementsFilter');1466 let elementsFilter = []1467 if (filterElement)1468 elementsFilter = filterElement.innerHTML.split(";");1469 1470 for (let i = 0; i < elements.length; i++) {1471 let f = elementsFilter[i];1472 1473 if (elements[i] !== "") {1474 1475 let eid = String(elementsID[i]);1476 if (selectedControlElements.indexOf(eid) === -1) {1477 1478 let published = parseInt(elementsPublished[i]);1479 1480 if (typeof f != "undefined") {1481 let f_list = f.split(",");1482 1483 if (f_list.indexOf(v) !== -1) {1484 let opt = document.createElement("option");1485 opt.value = eid;1486 if (eid === selectedValue)1487 opt.selected = true;1488 1489 if (published === 0)1490 opt.style.cssText = "color:red;";1491 1492 opt.innerHTML = elements[i];1493 l.appendChild(opt);1494 }1495 } else {1496 1497 let opt = document.createElement("option");1498 opt.value = eid;1499 if (eid === selectedValue)1500 opt.selected = true;1501 1502 if (published === 0)1503 opt.style.cssText = "color:red;";1504 1505 opt.innerHTML = elements[i];1506 l.appendChild(opt);1507 }1508 }1509 }1510 }1511 1512 return true;1513 }1514 1597 1515 1598 // ------------------------ Google Map coordinates … … 1571 1654 } 1572 1655 1573 let ct_signaturePad_fields = [];1574 let ct_signaturePad = [];1575 let ct_signaturePad_formats = [];1576 1577 function ctInputbox_signature(inputbox_id, width, height, format) {1578 1579 let canvas = document.getElementById(inputbox_id + '_canvas');1580 1581 ct_signaturePad_fields.push(inputbox_id);1582 ct_signaturePad[inputbox_id] = new SignaturePad(canvas, {1583 backgroundColor: "rgb(255, 255, 255)"1584 });1585 1586 ct_signaturePad_formats[inputbox_id] = format;1587 1588 canvas.width = width;1589 canvas.height = height;1590 canvas.getContext("2d").scale(1, 1);1591 1592 document.getElementById(inputbox_id + '_clear').addEventListener('click', function () {1593 ct_signaturePad[inputbox_id].clear();1594 });1595 }1596 1597 function ctInputbox_signature_apply() {1598 1599 if (ct_signaturePad_fields.length === 0)1600 return true;1601 1602 let inputbox_id = ct_signaturePad_fields[0];1603 1604 if (ct_signaturePad[inputbox_id].isEmpty()) {1605 alert(TranslateText('COM_CUSTOMTABLES_JS_SIGNATURE_REQUIRED'));1606 return false;1607 } else {1608 1609 let format = ct_signaturePad_formats[inputbox_id];1610 1611 //let dataURL = ct_signaturePad[inputbox_id].toDataURL('image/'+format+'+xml');1612 let dataURL = ct_signaturePad[inputbox_id].toDataURL('image/' + format);1613 document.getElementById(inputbox_id).setAttribute("value", dataURL);1614 return true;1615 }1616 }1617 1656 1618 1657 /* … … 1805 1844 1806 1845 if (link.length === 2)//to make sure that it will work in the back-end 1807 url = ctWebsiteRoot + 'administrator/index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&where=' + encodeURIComponent(where);1846 url = CTEditHelper.websiteRoot + 'administrator/index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&where=' + encodeURIComponent(where); 1808 1847 else 1809 url = ctWebsiteRoot + 'index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&where=' + encodeURIComponent(where);1848 url = CTEditHelper.websiteRoot + 'index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&where=' + encodeURIComponent(where); 1810 1849 1811 1850 } else if (CTEditHelper.cmsName === "WordPress") { 1812 url = ctWebsiteRoot + 'index.php?page=customtables-api-tablejoin&key=' + key + '&index=0&where=' + encodeURIComponent(where);1851 url = CTEditHelper.websiteRoot + 'index.php?page=customtables-api-tablejoin&key=' + key + '&index=0&where=' + encodeURIComponent(where); 1813 1852 console.error(url); 1814 1853 console.error("updateChildTableJoinField is going to be supported by WP yet."); … … 1844 1883 for (let i = 0; i < valueFiltersNames.length; i++) { 1845 1884 if (valueFiltersNames[i] !== null) { 1846 let value = response['record']['es_' + valueFiltersNames[i]]; 1847 NewValueFilters.push(value); 1885 console.warn("response", response['record']); 1886 console.warn("valueFiltersNames[i]", valueFiltersNames[i]); 1887 1888 let value = ""; 1889 if (response['record']) { 1890 value = response['record']['es_' + valueFiltersNames[i]]; 1891 NewValueFilters.push(value); 1892 } 1848 1893 1849 1894 let index = i - 1; … … 1874 1919 1875 1920 if (link.length === 2)//to make sure that it will work in the back-end 1876 url = ctWebsiteRoot + 'administrator/index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&limit=20&';1921 url = CTEditHelper.websiteRoot + 'administrator/index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&limit=20&'; 1877 1922 else 1878 url = ctWebsiteRoot + 'index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&limit=20&';1923 url = CTEditHelper.websiteRoot + 'index.php?option=com_customtables&view=catalog&tmpl=component&from=json&key=' + key + '&index=0&limit=20&'; 1879 1924 1880 1925 } else if (CTEditHelper.cmsName === "WordPress") { -
customtables/trunk/libraries/customtables/media/js/extratasks.js
r3219911 r3262394 54 54 function ctQueryAPI(task, old_params, new_params, tableid, fieldid) { 55 55 let parts = location.href.split("/administrator/"); 56 let websiteroot = parts[0] + "/administrator/"; 57 let url = websiteroot + "index.php?option=com_customtables&view=api&frmt=json&task=" + task + "&old_typeparams=" + old_params; 56 let url = parts[0] + "/administrator/index.php?option=com_customtables&view=api&frmt=json&task=" + task + "&old_typeparams=" + old_params; 58 57 url += "&tableid=" + tableid; 59 58 url += "&new_typeparams=" + new_params + "&fieldid=" + fieldid + "&startindex=" + extraTasksUpdate_startindex + "&stepsize=" + extraTasksUpdate_stepsize; -
customtables/trunk/libraries/customtables/media/js/layoutwizard.js
r3252609 r3262394 63 63 if (window.Joomla instanceof Object) { 64 64 const parts = location.href.split("/administrator/"); 65 const websiteRoot = parts[0] + "/administrator/"; 66 url = websiteRoot + "index.php?option=com_customtables&view=api&frmt=json&task=getfields&tableid=" + tableid; 65 url = parts[0] + "/administrator/index.php?option=com_customtables&view=api&frmt=json&task=getfields&tableid=" + tableid; 67 66 } else if (document.body.classList.contains('wp-admin') || document.querySelector('#wpadminbar')) { 68 67 let parts = location.href.split("wp-admin/admin.php?"); -
customtables/trunk/libraries/customtables/media/js/modal.js
r3202599 r3262394 148 148 for (let i = 0; i < scripts.length; i++) { 149 149 if (scripts[i].src === "") { 150 eval(scripts[i].innerHTML); 150 151 try { 152 eval(scripts[i].innerHTML); 153 } catch (error) { 154 console.warn("Executing script:", scripts[i].innerHTML); 155 console.warn("Error executing script:", error); 156 } 151 157 } 152 158 } 153 159 } 154 155 160 156 161 function ctHidePopUp() { -
customtables/trunk/libraries/customtables/media/js/uploader.js
r3219911 r3262394 8 8 **/ 9 9 10 const uploaderParams = []; 10 if (!window.uploaderParams) { // This works only if uploaderParams is defined globally. 11 window.uploaderParams = []; 12 } 11 13 12 14 function updateUploadedFileBox(index) { -
customtables/trunk/libraries/customtables/media/xml/tags.xml
r3250784 r3262394 959 959 <param name="parameter" label="Parameter" type="string" description="Url query parameter" 960 960 example="firstname"/> 961 </params> 962 </tag> 963 964 <tag name="getwhere" label="Query Where" startchar="{" endchar="}" 965 description="Generates and returns a URL query string segment for the 'where' parameter. This tag is useful when constructing URLs dynamically based on specific conditions or filtering criteria. For example, to retrieve entries created on a particular date, you can use: ?view=catalog&where=CreatedAt%3D2025-03-20." 966 twigclass="url" 967 wordpress="true"> 968 <params> 969 <param name="parameter" label="Parameter" type="string" 970 description="The name of the field to filter by within the URL query. This parameter should match the field name used in your database or catalog." 971 example="CreatedAt"/> 961 972 </params> 962 973 </tag> -
customtables/trunk/readme.txt
r3259510 r3262394 6 6 Requires at least: 6.0 7 7 Tested up to: 6.7.2 8 Stable tag: 1.5. 78 Stable tag: 1.5.8 9 9 Requires PHP: 7.4 10 10 License: GPLv2 … … 50 50 51 51 == Changelog == 52 53 = 1.5.8 = 54 55 - Fixed import issue when tables have different field prefixes. 56 - Fixed addForeignKey for user fields. 57 - Fixed removeForeignKey. 58 - {{ html.pagination }} added to Auto Layout Creator (WP) & default simple catalog layout. 59 - Search by exact date in datetime fields added. 60 - Date field search now applies global search filter. 61 - Fixed ordering by User Field in WP. 62 - Fixed current language detection in WP. 63 - Error message text improved for better clarity. 64 - Fixed domain validation bug in URL Field type. 65 - Fixed Lookup Table self-parent value selection issue. 66 - Fixed delete confirmation message (now correctly shows item name instead of ID). 67 - Prevented duplicate JS function declarations. 68 - Added new virtual field option: Stored Decimal. 52 69 53 70 = 1.5.7 =
Note: See TracChangeset
for help on using the changeset viewer.