Jump to content


Caspio Rockstar
  • Content Count

  • Joined

  • Last visited

  • Days Won


Reputation Activity

  1. Like
    Vitalikssssss got a reaction from RickManolo in Add text field entry to a text area   
    Hi @RickManolo,
    Well, for me the easiest way to do this is to use a Virtual field to display "Notes" and make an actual field "Notes" as hidden.
    I assume that you use Single Record Update or Details Datapage. 
    If the answer for above is yes, you can use the following JS code to concat existing Note with AddNote input after Update of the record.
    <script type="text/javascript"> document.addEventListener('BeforeFormSubmit', function (event) { let target = document.querySelector('[id*="EditRecordNotes"]'); let n_notes = document.querySelector('input[id*="EditRecordAdd_Note"]').value; let o_notes = document.querySelector('input[name*="cbParamVirtual1"]').value; target.value = o_notes + n_notes; }); </script> Change the name of the fields if necessary, and also make sure you disable HTML editor prior to pasting the code.
    Hope this helps.
  2. Like
    Vitalikssssss got a reaction from GWBjr in Record Level Access based on partial field match   

    Try this expression:
    'www.' + Substring([@field:Email], ((Charindex('@', [@field:Email]))+1), Len([@field:Email])) Regards,
  3. Thanks
    Vitalikssssss got a reaction from Ed727 in Line breaks in Text(64000) fields not recognized in html block insert   
    Hi @Ed727,
    I am afraid that long text without HTML tags  will be rendered as a single line string.
  4. Thanks
    Vitalikssssss reacted to kpcollier in Make JS Work With Empty Fields   
    Haha, wow. Your code looks SO nice, specially compared to the way I did it. Thank you @Vitalikssssss it works great.
  5. Thanks
    Vitalikssssss got a reaction from kpcollier in Make JS Work With Empty Fields   
    Hi @kpcollier,
    Try this code:
    <script type="text/javascript"> document.addEventListener('DataPageReady', function (event) { let v_fields = ["Labor1_Rate", "Labor1_Hours", "Labor2_Rate", "Labor2_Hours", "Labor3_Rate","Labor3_Hours", "Labor4_Rate", "Labor4_Hours", "Labor5_Rate", "Labor5_Hours", "Labor6_Rate", "Labor6_Hours", "Labor7_Rate", "Labor7_Hours", "Labor8_Rate", "Labor8_Hours", "Labor9_Rate", "Labor9_Hours", "Labor10_Rate", "Labor10_Hours", "Labor11_Rate", "Labor11_Hours", "Labor12_Rate", "Labor12_Hours", "Labor13_Rate", "Labor13_Hours", "Labor14_Rate", "Labor14_Hours", "Labor15_Rate", "Labor15_Hours", ]; let t_field = ["Labor1_Total", "Labor2_Total", "Labor3_Total", "Labor4_Total", "Labor5_Total", "Labor6_Total", "Labor7_Total", "Labor8_Total", "Labor9_Total", "Labor10_Total", "Labor11_Total", "Labor12_Total", "Labor13_Total", "Labor14_Total", "Labor15_Total",]; let sub_total = document.getElementById("InsertRecordLabor_SubTotal"); sub_total.value = 0; let v_state = []; let t_state = []; v_fields.forEach(function(el) { if (el !== null) { v_state.push(document.getElementById("InsertRecord"+el)); } }); v_state.forEach(function(el){ if (el !== null) { el.addEventListener("keyup", calculate); } }); t_field.forEach(function(el) { if (el !== null) { t_state.push(document.getElementById("InsertRecord"+el)); } }); function calculate() { let i = 0; t_state.forEach(element => { if (element !== null) { element.value = ((!isNaN(v_state[i].value)) ? v_state[i].value : 0) * ((!isNaN(v_state[i+1].value)) ? v_state[i+1].value : 0); sub_total.value = +sub_total.value + +element.value; console.log(sub_total.value); i+=2; } }); } }); </script>  
    This code should resolve the issue.
  6. Like
    Vitalikssssss got a reaction from kpcollier in Button To Copy Address - Cascading Elements   
    Hi @kpcollier,
    You can use "start with" type of comparison in query selector like this:
    document.querySelector("input[id^='InsertRecordPrimary_Address']").value; The whole code would look like this:
    <script type="text/javascript"> document.addEventListener('DataPageReady', function (event) { function f_address(){ if(document.getElementById('cbParamVirtual4').checked) { document.querySelector("input[id^='InsertRecordBilling_Address']").value = document.querySelector("input[id^='InsertRecordPrimary_Address']").value; document.querySelector("input[id^='InsertRecordBilling_City']").value = document.querySelector("input[id^='InsertRecordPrimary_City']").value; document.querySelector("input[id^='InsertRecordBilling_State']").value = document.querySelector("input[id^='InsertRecordPrimary_State']").value; document.querySelector("input[id^='InsertRecordBilling_Zip']").value = document.querySelector("input[id^='InsertRecordPrimary_Zip']").value; } else { document.querySelector("input[id^='InsertRecordBilling_Address']").value = ""; document.querySelector("input[id^='InsertRecordBilling_City']").value = ""; document.querySelector("input[id^='InsertRecordBilling_State']").value = ""; document.querySelector("input[id^='InsertRecordBilling_Zip']").value = ""; } } document.getElementById('cbParamVirtual4').onclick= f_address; }); </script>  
  7. Like
    Vitalikssssss got a reaction from deemuss in Set Check box with a formula?   
    Hi @scottd,
    Here is a topic with a solution:
    Hope this helps
  8. Thanks
    Vitalikssssss reacted to NiceDuck in Record authentication field upon delete   

    Thank you for the response Shiro and Vitaliksssss.
    I just want to share what I did to resolve this issue.  Instead of using the Caspio's default delete functions, what I did is I added a erase field which is a yes/no filed on my records and I created a trigger that will delete those records with 'yes' values on these fields.
    since the record is updated instead of deleted, the datapage will be able to capture the authentication fields first before the trigger delete the record. I will simplify my workflow as this
    check the yes/no field assign for deletion --> update the record --> record is updated/inserted on the table --> trigger will check for records with a 'yes' value on its erase field --> delete those records.
    This works on inline edit, bulk edit and details pages. However, if you deleted a record from Caspio Bridge, it will not be recorded on your history log. 
    Hope this helps anyone.
  9. Like
    Vitalikssssss got a reaction from Corpcatalog in Line Chart time series   
    Hi @Corpcatalog,
    You can build such Chart in Caspio but you would need to modify your table first.
    You table should look like the following:

    You can build the following Chart once you transform your table:

    You may import attached Datapage to your account in order to see the settings for the Chart Datapage.
    Hope this helps.
  10. Thanks
    Vitalikssssss reacted to Alison in Set a placeholder to the Multiselect Dropdown form element   
    I want to share a solution on how to set a  placeholder to the Multiselect Dropdown form element on the search form.
    You should put the following code into the Footer after disabling HTML Editor on the advanced tab:
    <script> document.addEventListener('DataPageReady', function () { document.querySelector('[id^="ComboBoxValue"]').setAttribute("placeholder", "Your value for the placeholder"); }); </script>
  11. Thanks
    Vitalikssssss reacted to Hastur in XIRR function   
    Hello @Vitalikssssss!
    It is possible to implement such a formula within Tabular report using additional JS.

    Please follow these steps:
    1. You need to create additional APP parameter to use the "moment.js" library.
    Please check this article to get familiar with the APP Parameters in Caspio - https://howto.caspio.com/apps/app-parameters/

    2. Create the Tabular Report and add the Header/Footer. Please insert this code into the Header:
    <div style='display:flex; justify-content:flex-start;padding:30px;'> <input placeholder="Guess Rate, %" id='rate'></input> <button id='click' class='cbResultSetAddButton' style='margin-left:10px;'>Calculate XIRR</button> <div id='result' style='margin-left:10px;'></div> </div> <script src="[@app:URL_1]"></script> <script> "use strict"; document.addEventListener('DataPageReady', function () { document.querySelector('#click').addEventListener('click', function (event) { var formatDate = 'DD/MM/YYYY'; var columnDateNumber = 2; //Change based on your column position on a result set var columnValuesNumber = 3; //Change based on your column position on a result set //Not changeable part var datesSelector = "tr>td:nth-child(".concat(columnDateNumber, ")[class^=\"cbResultSetData\"]"); var valuesSelector = "tr>td:nth-child(".concat(columnValuesNumber, ")[class^=\"cbResultSetData\"]"); var dates = []; var values = []; document.querySelectorAll(datesSelector).forEach(function (item, index) { if (!item.hasAttribute('style')) { return dates.push(item.innerText); } }); document.querySelectorAll(valuesSelector).forEach(function (item, index) { if (!item.hasAttribute('style')) { return values.push(+item.innerText); } }); var guess = +document.querySelector('#rate').value; var some; if (guess != 0) { some = guess/100; } document.querySelector('#result').innerHTML = "Result:<strong>".concat(XIRR(values, dates, some, formatDate)*100, "%</strong>"); }); }); function XIRR(values, dates, guess, local) { // Credits: algorithm inspired by Apache OpenOffice // Calculates the resulting amount var irrResult = function irrResult(values, dates, rate) { var r = rate + 1; var result = values[0]; for (var i = 1; i < values.length; i++) { result += values[i] / Math.pow(r, moment(dates[i], local).diff(moment(dates[0], local), "days") / 365); } return result; }; // Calculates the first derivation var irrResultDeriv = function irrResultDeriv(values, dates, rate) { var r = rate + 1; var result = 0; for (var i = 1; i < values.length; i++) { var frac = moment(dates[i], local).diff(moment(dates[0], local), "days") / 365; result -= frac * values[i] / Math.pow(r, frac + 1); } return result; }; // Check that values contains at least one positive value and one negative value var positive = false; var negative = false; for (var i = 0; i < values.length; i++) { if (values[i] > 0) positive = true; if (values[i] < 0) negative = true; } // Return error if values does not contain at least one positive value and one negative value if (!positive || !negative) return "#NUM!"; // Initialize guess and resultRate var guess = typeof guess === "undefined" ? 0.1 : guess; var resultRate = guess; // Set maximum epsilon for end of iteration var epsMax = 1e-10; // Set maximum number of iterations var iterMax = 50; // Implement Newton's method var newRate, epsRate, resultValue; var iteration = 0; var contLoop = true; do { resultValue = irrResult(values, dates, resultRate); newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate); epsRate = Math.abs(newRate - resultRate); resultRate = newRate; contLoop = epsRate > epsMax && Math.abs(resultValue) > epsMax; } while (contLoop && ++iteration < iterMax); if (contLoop) return "#NUM!"; // Return internal rate of return return resultRate; } </script> You should change the selectors of your Date and Value fields, if they are placed in the different position.
    Selectors can be changed here: 
    var columnDateNumber = 2; //Change based on your column position on a result set var columnValuesNumber = 3; //Change based on your column position on a result set Please find the print screen attached.

    Do not forget to Disable the HTML editor.

    Also, find the dummy application with this customization attached - XIRR_Function_1_0_2019-Aug-22_1503.zip
    Please let me know if you need any assistance.
  12. Thanks
    Vitalikssssss got a reaction from SonoftheSun in Total Hours Worked / Datediff   
    I have tweaked the formula provided by @MayMusic further since some time I need zero in front of single-digit hour e.g. 7:00.
    So, for the total&aggeration field it looks like this:
    (CASE WHEN LEN(CAST ((SUM(DATEDIFF(SECOND, [@field:Start_time], [@field:End_time]))/3600) AS VARCHAR(6))) < 2 THEN '0'+ CAST ((SUM(DATEDIFF(SECOND, [@field:Start_time], [@field:Hora_fin]))/3600) AS VARCHAR(6)) ELSE CAST ((SUM(DATEDIFF(SECOND, [@field:Start_time], [@field:Hora_fin]))/3600) AS VARCHAR(6)) END) + ':' + (CASE WHEN LEN(CAST ((SUM(DATEDIFF(SECOND, [@field:Start_time], [@field:Hora_fin]))%3600/60) AS VARCHAR(6))) < 2 THEN '0'+ CAST ((SUM(DATEDIFF(SECOND, [@field:Start_time], [@field:Hora_fin]))%3600/60) AS VARCHAR(6)) ELSE CAST ((SUM(DATEDIFF(SECOND, [@field:Start_time], [@field:Hora_fin]))%3600/60) AS VARCHAR(6)) END) Hope it would help someone.
  13. Thanks
    Vitalikssssss got a reaction from kpcollier in Button to Submit Name   
    Hi @kpcollier,
    I have seen similar behavior on Caspio Ready Made App called Resource scheduling.
    Login in as a member, perform a simple search and you will see a Tabular Report with "Reserve this Item" button which performs similar logic.
    I believe you need the following items in order to create similarly flow within your app:
    1. Single record update form with default elements hidden by CSS.
    2. Embed this DP in the HTML block of the report by using the Iframe deployment method.
    I need to mention that DP performance might decrease if you have a lot of records on Result set (e.g. 250 per page).
    Hope this helps.
  14. Thanks
    Vitalikssssss reacted to MayMusic in Total Hours Worked / Datediff   
    Count the total mins either in the table or as cal field on your report. Here we call it TimeSpan


    Datediff(minute,[@field:StartTime], [@field:EndTime])


    Then in the aggregation use that to get the total:


    CAST ((SUM(TimeSpan)/60) AS VARCHAR(6)) + ':' +CAST ((SUM(TimeSpan)%60) AS VARCHAR(2))

  15. Thanks
    Vitalikssssss reacted to Hastur in Chart Datapage   
    Hello @Vitalikssssss
    You can implement such a chart using the Highchart library and Tabular Report Datapage.
    1. Create the Tabular Report Datapage based on the table you want to use to draw a chart.
    2. Use predefined criteria to find the actual row from the table.
    3. Add all the fields you want to draw to ResultSet.
    5. Add two APP parameters to your Application. Find the files attached. Name the files in the same way as it is on the screenshot below.

    6. Add the following code to Header/Footer of the result set page.
    <script src="[@app:JQueryScript/]"></script> <script src="[@app:HighchartsScript/]"></script> <div id="container" style="height: 400px"></div> <style> .cbResultSetTableCellNumberDate, .cbResultSetTotalsDataCellNumberDate { text-align: left !important; } form[action^="https://c1abc032.caspio.com/dp/9a23600092ea09cd1acf435e847b"] { display: none; } </style> You should only change the DeployURL in this snippet of code.
    Use the Deploy URL of the same Datapage you work on after you create it.
    <script type="text/javascript"> document.addEventListener('DataPageReady', function () { /* ________ Check if Highcharts library is loaded ________ */ var myVar = setInterval(myTimer, 200); function myTimer() { console.log(typeof Highcharts); if(typeof Highcharts !== "undefined") { myStopFunction(myVar); drawChart(); } } function myStopFunction(val) { clearInterval(val); } /* ________ Draw Chart function ________ */ function drawChart() { let labelsRow = document.querySelectorAll('.cbResultSetLabelLink'); let labels = []; let valuesRow = document.querySelectorAll('.cbResultSetTableCellNumberDate'); let values = []; let resultSet = []; for(let i = 0; i < labelsRow.length; i++) { labels .push(labelsRow[i].innerText); values.push(+valuesRow[i].innerText); } for(let i = 0; i < values.length; i++) { if(values[i] === 0) { continue; } else { resultSet.push({ name: labels[i], data: [ values[i] ] }); } } var chart = Highcharts.chart('container', { chart: { type: 'column' }, title: { text: 'Comparosin chart' }, xAxis: { labels: { enabled: false } }, yAxis: { min: 0, title: { text: 'Value' } }, credits: { enabled: false }, series: resultSet }); } }); </script>  
    highcharts.js jquery.js
  16. Thanks
    Vitalikssssss reacted to DefinitelyNot31337 in Login after User Registration   
    Hello @TroubleShooter,
    Yes, you can. Currently, it is only possible with JavaScript.
    The idea is to place the Registration Form and Standalone Login Page side-by-side (or at least, in the same page).
    You may hide the Login Form ( using <div style="display: none;"></div> on the Header/Footer of the DataPage || More about here...), on your Registration Page but I rather keep them visible so the users can opt to just log-in if they already have an account; or register, if they don't.
    After registration, JavaScript will fill-out the login form, and submits it programatically, therefore logging-in the newly registered user.
    The only requirements to this implementation are:
    1.) Login DataPage and Registration Form DataPage are deployed on the same page.
    2.) Both DataPages are AJAX-Enabled!
    3.) Headers/Footers should have the HTML Editor disabled from the Advanced tab.
    Without futher ado, let's do this.
    1.) Open DataPage Configuration for your Registration Page:
    2.) Go to 'Configure Fields Section'
    2.1.) Add a Header and Footer (Disable HTML Editor from the advanced tab)
    2.2.) Paste the code snippet in the Footer and modify the following information.
    <script> document.addEventListener('BeforeFormSubmit', function() { var registrationDP = "[@cbAppKey]"; var loginDP = "378fd3458dfsjhefjhqerwfdsyui3274239"; //Replace with the AppKey of your Standalone Login Scren var username_field = "username"; //replace with the name of your username field (CASE SENSITIVE!) var password_field = "password"; //replace with the name of your password field (CASE SENSITIVE!) //No modifications necessary from this point onward. document.querySelector(`[action*="${loginDP}"] [id*=xip_username]`).value = document.querySelector(`[action*="${registrationDP}"] [id*=InsertRecord${username_field}]`).value document.querySelector(`[action*="${loginDP}"] [id*=xip_password]`).value = document.querySelector(`[action*="${registrationDP}"] [id*=InsertRecord${password_field}]`).value }) </script>  
    5.) Go to 'Destination and Messaging'
    5.5) Set Destination after record submit to: Display a message; Disable the HMTL Editor!
    5.6) Paste the code snippet below; then modify.
    Registered Successfully. <script> var loginDP = "378fd3458dfsjhefjhqerwfdsyui3274239"; document.querySelector(`[action*="${loginDP}"]`).submit(); </script>  
    After saving, you should be good to go.
    Working example: https://stage.caspio.com/support/forums/DefinitelyNot31337/register-login/index.html
    Application Export Package: CaspioData_2019-Apr-02_0219.zip
    Good luck and happy hacking!
  17. Like
    Vitalikssssss got a reaction from kpcollier in Checkbox In Search To Include Additional Results   
    Hi @kpcollier,
    I believe you can switch the comparison operator for "Tech_Status" to "Not Equal".

    Hope this helps.
  18. Like
    Vitalikssssss got a reaction from kpcollier in 'Freeze' Field When Given A Value   
    Hi @kpcollier,
    You can check the status before the update to make sure it is not equal to the list of previous statuses. 
    Here is an example for one of the Trigger blocks:

    Hope this helps.
  19. Thanks
    Vitalikssssss got a reaction from JEllington in Create multiple records in “child” table conditionally   
    Hi @Alison,
    You can design Trigger in such a way which allow you to achieve the desired functionality.
    You need to make sure that:
    1. Your parent/child table has one-to-many relationship;
    2. You create a special table which will be used as insert template.
    3. Please note that you would need to list the same number of records in insert template as the number of days which you could have in the date range.
    4. You need to create a Trigger.
    You may import my example into your account and check the structure there. 
  20. Thanks
    Vitalikssssss reacted to Alison in Add a checkbox "Remember me"   
    Hi @deemuss,

    Yes, it can be done with the jQuery code.  You should go to the editing Authentication screen, where there are two fields - Name and Password, for example. Add header and footer element and HTML block.

    Put the following code to the header element for jQuery library: 
    <script src="https://code.jquery.com/jquery-1.9.1.js"></script> Put the following code into the HTML block for the "Remember me" checkbox:
    <label class="checkbox"> <input type="checkbox" value="remember-me" id="remember_me"> Remember me </label>
    Put the following code into the footer element for the jQuery code:
    <script> $(function() { if (localStorage.chkbx && localStorage.chkbx != '') { $('#remember_me').attr('checked', 'checked'); $('#xip_Name').val(localStorage.usrname); $('#xip_Password').val(localStorage.pass); } else { $('#remember_me').removeAttr('checked'); $('#xip_Name').val(''); $('#xip_Password').val(''); } $('#remember_me').click(function() { if ($('#remember_me').is(':checked')) { // save username and password localStorage.usrname = $('#xip_Name').val(); localStorage.pass = $('#xip_Password').val(); localStorage.chkbx = $('#remember_me').val(); } else { localStorage.usrname = ''; localStorage.pass = ''; localStorage.chkbx = ''; } }); }); </script> where  #xip_Name is an ID for the Name field and #xip_Password is an ID for the Password field.

    Please see this screenshot for better understanding:

  21. Thanks
    Vitalikssssss reacted to George43 in Dropdown customization   
    You should Add Event Listener on that particular Input and apply style to all child Nodes
    <script type="text/javascript"> document.addEventListener('DataPageReady', function(event) { document.querySelectorAll("[name~='InlineAddemail']")[0].addEventListener('click', function(){ if(document.querySelectorAll('.Ctnr')[0]){ document.getElementsByClassName('DropBox')[0].style.setProperty('width', '350px'); document.querySelectorAll('.Option').forEach(function(elem){ elem.style.setProperty('border-bottom', '1px solid black'); elem.style.fontFamily = 'Lucida Console'; }); } }); }); </script> InlineAddemail is your Input name attribute. You can find it via chrome developer console.
  22. Like
    Vitalikssssss got a reaction from Harbinger in maintain user session across multiple pages   
    Hi @ministry,
    You can achieve the desired functionality with standard functionality of Caspio.
    You need to create a separate authentication for each user group: "Normal", "Admin" and use this authentication on Datapages which are relevant to each group. You may check this video for more insights:
    Hope this helps.
  23. Like
    Vitalikssssss got a reaction from dmyoungsal in Relationship not working (2)   
    Hi @dmyoungsal,
    Please make sure that you select a "Display Value" in the relationship settings. 

    Hope this helps.
  24. Thanks
    Vitalikssssss reacted to vanderLeest in Default Values for Inline Insert   
    I found a way to do it by adding a small function to the javascript to decode html entities:
    function decodeEntities(encodedString) {
        var textArea = document.createElement('textarea');
        textArea.innerHTML = encodedString;
        return textArea.value;
    // Replace [@authfield:name] accordingly with the parameter or custom value you wish.
    var paramValue = '[@XC_Organization]';

    // Replace with the name of your field as described in your DataSource
    var field_name = 'XC_Organization';

    /* Edits are not necessary for this part */
    var i_field = document.querySelector('form[action*="[@cbAppKey]"] #InlineAdd' + field_name);
    i_field.value = decodeEntities(paramValue);

    /* To this part*/
  25. Thanks
    Vitalikssssss got a reaction from kpcollier in Checkbox/Radio Buttons for Search Field Blank/Not Blank   
    Hi @kpcollier,
    I think you can create a Formula field in your table with a case expression to check if the field "Shipping Confirmation" is empty or not.
    Here is an example of Formula expression:
    CASE WHEN Len([@field:Shipping_Date])>0 THEN 'YES' ELSE 'NO' END Next, you should use this field in your search criteria and assign "Checkbox" form element. 
    You should use the following settings:

    Hope this helps.
  • Create New...